iced_native/widget/pane_grid/
state.rs

1//! The state of a [`PaneGrid`].
2//!
3//! [`PaneGrid`]: crate::widget::PaneGrid
4use crate::widget::pane_grid::{
5    Axis, Configuration, Direction, Node, Pane, Split,
6};
7use crate::{Point, Size};
8
9use std::collections::HashMap;
10
11/// The state of a [`PaneGrid`].
12///
13/// It keeps track of the state of each [`Pane`] and the position of each
14/// [`Split`].
15///
16/// The [`State`] needs to own any mutable contents a [`Pane`] may need. This is
17/// why this struct is generic over the type `T`. Values of this type are
18/// provided to the view function of [`PaneGrid::new`] for displaying each
19/// [`Pane`].
20///
21/// [`PaneGrid`]: crate::widget::PaneGrid
22/// [`PaneGrid::new`]: crate::widget::PaneGrid::new
23#[derive(Debug, Clone)]
24pub struct State<T> {
25    /// The panes of the [`PaneGrid`].
26    ///
27    /// [`PaneGrid`]: crate::widget::PaneGrid
28    pub panes: HashMap<Pane, T>,
29
30    /// The internal state of the [`PaneGrid`].
31    ///
32    /// [`PaneGrid`]: crate::widget::PaneGrid
33    pub internal: Internal,
34
35    /// The maximized [`Pane`] of the [`PaneGrid`].
36    ///
37    /// [`PaneGrid`]: crate::widget::PaneGrid
38    pub(super) maximized: Option<Pane>,
39}
40
41impl<T> State<T> {
42    /// Creates a new [`State`], initializing the first pane with the provided
43    /// state.
44    ///
45    /// Alongside the [`State`], it returns the first [`Pane`] identifier.
46    pub fn new(first_pane_state: T) -> (Self, Pane) {
47        (
48            Self::with_configuration(Configuration::Pane(first_pane_state)),
49            Pane(0),
50        )
51    }
52
53    /// Creates a new [`State`] with the given [`Configuration`].
54    pub fn with_configuration(config: impl Into<Configuration<T>>) -> Self {
55        let mut panes = HashMap::new();
56
57        let internal =
58            Internal::from_configuration(&mut panes, config.into(), 0);
59
60        State {
61            panes,
62            internal,
63            maximized: None,
64        }
65    }
66
67    /// Returns the total amount of panes in the [`State`].
68    pub fn len(&self) -> usize {
69        self.panes.len()
70    }
71
72    /// Returns `true` if the amount of panes in the [`State`] is 0.
73    pub fn is_empty(&self) -> bool {
74        self.len() == 0
75    }
76
77    /// Returns the internal state of the given [`Pane`], if it exists.
78    pub fn get(&self, pane: &Pane) -> Option<&T> {
79        self.panes.get(pane)
80    }
81
82    /// Returns the internal state of the given [`Pane`] with mutability, if it
83    /// exists.
84    pub fn get_mut(&mut self, pane: &Pane) -> Option<&mut T> {
85        self.panes.get_mut(pane)
86    }
87
88    /// Returns an iterator over all the panes of the [`State`], alongside its
89    /// internal state.
90    pub fn iter(&self) -> impl Iterator<Item = (&Pane, &T)> {
91        self.panes.iter()
92    }
93
94    /// Returns a mutable iterator over all the panes of the [`State`],
95    /// alongside its internal state.
96    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&Pane, &mut T)> {
97        self.panes.iter_mut()
98    }
99
100    /// Returns the layout of the [`State`].
101    pub fn layout(&self) -> &Node {
102        &self.internal.layout
103    }
104
105    /// Returns the adjacent [`Pane`] of another [`Pane`] in the given
106    /// direction, if there is one.
107    pub fn adjacent(&self, pane: &Pane, direction: Direction) -> Option<Pane> {
108        let regions = self
109            .internal
110            .layout
111            .pane_regions(0.0, Size::new(4096.0, 4096.0));
112
113        let current_region = regions.get(pane)?;
114
115        let target = match direction {
116            Direction::Left => {
117                Point::new(current_region.x - 1.0, current_region.y + 1.0)
118            }
119            Direction::Right => Point::new(
120                current_region.x + current_region.width + 1.0,
121                current_region.y + 1.0,
122            ),
123            Direction::Up => {
124                Point::new(current_region.x + 1.0, current_region.y - 1.0)
125            }
126            Direction::Down => Point::new(
127                current_region.x + 1.0,
128                current_region.y + current_region.height + 1.0,
129            ),
130        };
131
132        let mut colliding_regions =
133            regions.iter().filter(|(_, region)| region.contains(target));
134
135        let (pane, _) = colliding_regions.next()?;
136
137        Some(*pane)
138    }
139
140    /// Splits the given [`Pane`] into two in the given [`Axis`] and
141    /// initializing the new [`Pane`] with the provided internal state.
142    pub fn split(
143        &mut self,
144        axis: Axis,
145        pane: &Pane,
146        state: T,
147    ) -> Option<(Pane, Split)> {
148        let node = self.internal.layout.find(pane)?;
149
150        let new_pane = {
151            self.internal.last_id = self.internal.last_id.checked_add(1)?;
152
153            Pane(self.internal.last_id)
154        };
155
156        let new_split = {
157            self.internal.last_id = self.internal.last_id.checked_add(1)?;
158
159            Split(self.internal.last_id)
160        };
161
162        node.split(new_split, axis, new_pane);
163
164        let _ = self.panes.insert(new_pane, state);
165        let _ = self.maximized.take();
166
167        Some((new_pane, new_split))
168    }
169
170    /// Swaps the position of the provided panes in the [`State`].
171    ///
172    /// If you want to swap panes on drag and drop in your [`PaneGrid`], you
173    /// will need to call this method when handling a [`DragEvent`].
174    ///
175    /// [`PaneGrid`]: crate::widget::PaneGrid
176    /// [`DragEvent`]: crate::widget::pane_grid::DragEvent
177    pub fn swap(&mut self, a: &Pane, b: &Pane) {
178        self.internal.layout.update(&|node| match node {
179            Node::Split { .. } => {}
180            Node::Pane(pane) => {
181                if pane == a {
182                    *node = Node::Pane(*b);
183                } else if pane == b {
184                    *node = Node::Pane(*a);
185                }
186            }
187        });
188    }
189
190    /// Resizes two panes by setting the position of the provided [`Split`].
191    ///
192    /// The ratio is a value in [0, 1], representing the exact position of a
193    /// [`Split`] between two panes.
194    ///
195    /// If you want to enable resize interactions in your [`PaneGrid`], you will
196    /// need to call this method when handling a [`ResizeEvent`].
197    ///
198    /// [`PaneGrid`]: crate::widget::PaneGrid
199    /// [`ResizeEvent`]: crate::widget::pane_grid::ResizeEvent
200    pub fn resize(&mut self, split: &Split, ratio: f32) {
201        let _ = self.internal.layout.resize(split, ratio);
202    }
203
204    /// Closes the given [`Pane`] and returns its internal state and its closest
205    /// sibling, if it exists.
206    pub fn close(&mut self, pane: &Pane) -> Option<(T, Pane)> {
207        if self.maximized == Some(*pane) {
208            let _ = self.maximized.take();
209        }
210
211        if let Some(sibling) = self.internal.layout.remove(pane) {
212            self.panes.remove(pane).map(|state| (state, sibling))
213        } else {
214            None
215        }
216    }
217
218    /// Maximize the given [`Pane`]. Only this pane will be rendered by the
219    /// [`PaneGrid`] until [`Self::restore()`] is called.
220    ///
221    /// [`PaneGrid`]: crate::widget::PaneGrid
222    pub fn maximize(&mut self, pane: &Pane) {
223        self.maximized = Some(*pane);
224    }
225
226    /// Restore the currently maximized [`Pane`] to it's normal size. All panes
227    /// will be rendered by the [`PaneGrid`].
228    ///
229    /// [`PaneGrid`]: crate::widget::PaneGrid
230    pub fn restore(&mut self) {
231        let _ = self.maximized.take();
232    }
233
234    /// Returns the maximized [`Pane`] of the [`PaneGrid`].
235    ///
236    /// [`PaneGrid`]: crate::widget::PaneGrid
237    pub fn maximized(&self) -> Option<Pane> {
238        self.maximized
239    }
240}
241
242/// The internal state of a [`PaneGrid`].
243///
244/// [`PaneGrid`]: crate::widget::PaneGrid
245#[derive(Debug, Clone)]
246pub struct Internal {
247    layout: Node,
248    last_id: usize,
249}
250
251impl Internal {
252    /// Initializes the [`Internal`] state of a [`PaneGrid`] from a
253    /// [`Configuration`].
254    ///
255    /// [`PaneGrid`]: crate::widget::PaneGrid
256    pub fn from_configuration<T>(
257        panes: &mut HashMap<Pane, T>,
258        content: Configuration<T>,
259        next_id: usize,
260    ) -> Self {
261        let (layout, last_id) = match content {
262            Configuration::Split { axis, ratio, a, b } => {
263                let Internal {
264                    layout: a,
265                    last_id: next_id,
266                    ..
267                } = Self::from_configuration(panes, *a, next_id);
268
269                let Internal {
270                    layout: b,
271                    last_id: next_id,
272                    ..
273                } = Self::from_configuration(panes, *b, next_id);
274
275                (
276                    Node::Split {
277                        id: Split(next_id),
278                        axis,
279                        ratio,
280                        a: Box::new(a),
281                        b: Box::new(b),
282                    },
283                    next_id + 1,
284                )
285            }
286            Configuration::Pane(state) => {
287                let id = Pane(next_id);
288                let _ = panes.insert(id, state);
289
290                (Node::Pane(id), next_id + 1)
291            }
292        };
293
294        Self { layout, last_id }
295    }
296}
297
298/// The current action of a [`PaneGrid`].
299///
300/// [`PaneGrid`]: crate::widget::PaneGrid
301#[derive(Debug, Clone, Copy, PartialEq)]
302pub enum Action {
303    /// The [`PaneGrid`] is idle.
304    ///
305    /// [`PaneGrid`]: crate::widget::PaneGrid
306    Idle,
307    /// A [`Pane`] in the [`PaneGrid`] is being dragged.
308    ///
309    /// [`PaneGrid`]: crate::widget::PaneGrid
310    Dragging {
311        /// The [`Pane`] being dragged.
312        pane: Pane,
313        /// The starting [`Point`] of the drag interaction.
314        origin: Point,
315    },
316    /// A [`Split`] in the [`PaneGrid`] is being dragged.
317    ///
318    /// [`PaneGrid`]: crate::widget::PaneGrid
319    Resizing {
320        /// The [`Split`] being dragged.
321        split: Split,
322        /// The [`Axis`] of the [`Split`].
323        axis: Axis,
324    },
325}
326
327impl Action {
328    /// Returns the current [`Pane`] that is being dragged, if any.
329    pub fn picked_pane(&self) -> Option<(Pane, Point)> {
330        match *self {
331            Action::Dragging { pane, origin, .. } => Some((pane, origin)),
332            _ => None,
333        }
334    }
335
336    /// Returns the current [`Split`] that is being dragged, if any.
337    pub fn picked_split(&self) -> Option<(Split, Axis)> {
338        match *self {
339            Action::Resizing { split, axis, .. } => Some((split, axis)),
340            _ => None,
341        }
342    }
343}
344
345impl Internal {
346    /// The layout [`Node`] of the [`Internal`] state
347    pub fn layout(&self) -> &Node {
348        &self.layout
349    }
350}