From d1f2a184394e0439db58918f88f51745fdaf59fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 7 May 2020 00:40:07 +0200 Subject: [PATCH 1/6] Implement `Clone` for `pane_grid::State` --- native/src/widget/pane_grid/state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs index ed2813b8..6bb3fd9c 100644 --- a/native/src/widget/pane_grid/state.rs +++ b/native/src/widget/pane_grid/state.rs @@ -21,7 +21,7 @@ use std::collections::HashMap; /// [`Pane`]: struct.Pane.html /// [`Split`]: struct.Split.html /// [`State`]: struct.State.html -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct State { pub(super) panes: HashMap, pub(super) internal: Internal, @@ -248,7 +248,7 @@ impl State { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Internal { layout: Node, last_id: usize, From f3d54a0f332c8da3aca3cc1b37e63cfa96d7a526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 9 May 2020 00:16:07 +0200 Subject: [PATCH 2/6] Expose `Node` and `State::layout` for `PaneGrid` --- native/src/widget/pane_grid.rs | 1 + native/src/widget/pane_grid/node.rs | 185 +++++++++++++++------------ native/src/widget/pane_grid/state.rs | 7 + wgpu/src/widget/pane_grid.rs | 4 +- 4 files changed, 113 insertions(+), 84 deletions(-) diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index cb623b29..4c0eeed2 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -17,6 +17,7 @@ mod state; pub use axis::Axis; pub use direction::Direction; +pub use node::Node; pub use pane::Pane; pub use split::Split; pub use state::{Focus, State}; diff --git a/native/src/widget/pane_grid/node.rs b/native/src/widget/pane_grid/node.rs index 4d5970b8..1b6633fa 100644 --- a/native/src/widget/pane_grid/node.rs +++ b/native/src/widget/pane_grid/node.rs @@ -5,12 +5,12 @@ use crate::{ use std::collections::HashMap; -#[derive(Debug, Clone, Hash)] +#[derive(Debug, Clone)] pub enum Node { Split { id: Split, axis: Axis, - ratio: u32, + ratio: f32, a: Box, b: Box, }, @@ -18,79 +18,6 @@ pub enum Node { } impl Node { - pub fn find(&mut self, pane: &Pane) -> Option<&mut Node> { - match self { - Node::Split { a, b, .. } => { - a.find(pane).or_else(move || b.find(pane)) - } - Node::Pane(p) => { - if p == pane { - Some(self) - } else { - None - } - } - } - } - - pub fn split(&mut self, id: Split, axis: Axis, new_pane: Pane) { - *self = Node::Split { - id, - axis, - ratio: 500_000, - a: Box::new(self.clone()), - b: Box::new(Node::Pane(new_pane)), - }; - } - - pub fn update(&mut self, f: &impl Fn(&mut Node)) { - match self { - Node::Split { a, b, .. } => { - a.update(f); - b.update(f); - } - _ => {} - } - - f(self); - } - - pub fn resize(&mut self, split: &Split, percentage: f32) -> bool { - match self { - Node::Split { - id, ratio, a, b, .. - } => { - if id == split { - *ratio = (percentage * 1_000_000.0).round() as u32; - - true - } else if a.resize(split, percentage) { - true - } else { - b.resize(split, percentage) - } - } - Node::Pane(_) => false, - } - } - - pub fn remove(&mut self, pane: &Pane) -> Option { - match self { - Node::Split { a, b, .. } => { - if a.pane() == Some(*pane) { - *self = *b.clone(); - Some(self.first_pane()) - } else if b.pane() == Some(*pane) { - *self = *a.clone(); - Some(self.first_pane()) - } else { - a.remove(pane).or_else(|| b.remove(pane)) - } - } - Node::Pane(_) => None, - } - } - pub fn regions( &self, spacing: f32, @@ -133,14 +60,87 @@ impl Node { splits } - pub fn pane(&self) -> Option { + pub(crate) fn find(&mut self, pane: &Pane) -> Option<&mut Node> { + match self { + Node::Split { a, b, .. } => { + a.find(pane).or_else(move || b.find(pane)) + } + Node::Pane(p) => { + if p == pane { + Some(self) + } else { + None + } + } + } + } + + pub(crate) fn split(&mut self, id: Split, axis: Axis, new_pane: Pane) { + *self = Node::Split { + id, + axis, + ratio: 0.5, + a: Box::new(self.clone()), + b: Box::new(Node::Pane(new_pane)), + }; + } + + pub(crate) fn update(&mut self, f: &impl Fn(&mut Node)) { + match self { + Node::Split { a, b, .. } => { + a.update(f); + b.update(f); + } + _ => {} + } + + f(self); + } + + pub(crate) fn resize(&mut self, split: &Split, percentage: f32) -> bool { + match self { + Node::Split { + id, ratio, a, b, .. + } => { + if id == split { + *ratio = percentage; + + true + } else if a.resize(split, percentage) { + true + } else { + b.resize(split, percentage) + } + } + Node::Pane(_) => false, + } + } + + pub(crate) fn remove(&mut self, pane: &Pane) -> Option { + match self { + Node::Split { a, b, .. } => { + if a.pane() == Some(*pane) { + *self = *b.clone(); + Some(self.first_pane()) + } else if b.pane() == Some(*pane) { + *self = *a.clone(); + Some(self.first_pane()) + } else { + a.remove(pane).or_else(|| b.remove(pane)) + } + } + Node::Pane(_) => None, + } + } + + fn pane(&self) -> Option { match self { Node::Split { .. } => None, Node::Pane(pane) => Some(*pane), } } - pub fn first_pane(&self) -> Pane { + fn first_pane(&self) -> Pane { match self { Node::Split { a, .. } => a.first_pane(), Node::Pane(pane) => *pane, @@ -157,9 +157,8 @@ impl Node { Node::Split { axis, ratio, a, b, .. } => { - let ratio = *ratio as f32 / 1_000_000.0; let (region_a, region_b) = - axis.split(current, ratio, halved_spacing); + axis.split(current, *ratio, halved_spacing); a.compute_regions(halved_spacing, ®ion_a, regions); b.compute_regions(halved_spacing, ®ion_b, regions); @@ -184,11 +183,10 @@ impl Node { b, id, } => { - let ratio = *ratio as f32 / 1_000_000.0; let (region_a, region_b) = - axis.split(current, ratio, halved_spacing); + axis.split(current, *ratio, halved_spacing); - let _ = splits.insert(*id, (*axis, *current, ratio)); + let _ = splits.insert(*id, (*axis, *current, *ratio)); a.compute_splits(halved_spacing, ®ion_a, splits); b.compute_splits(halved_spacing, ®ion_b, splits); @@ -197,3 +195,26 @@ impl Node { } } } + +impl std::hash::Hash for Node { + fn hash(&self, state: &mut H) { + match self { + Node::Split { + id, + axis, + ratio, + a, + b, + } => { + id.hash(state); + axis.hash(state); + ((ratio * 100_000.0) as u32).hash(state); + a.hash(state); + b.hash(state); + } + Node::Pane(pane) => { + pane.hash(state); + } + } + } +} diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs index 6bb3fd9c..5cb41984 100644 --- a/native/src/widget/pane_grid/state.rs +++ b/native/src/widget/pane_grid/state.rs @@ -102,6 +102,13 @@ impl State { self.panes.iter_mut() } + /// Returns the layout tree stored in the [`State`]. + /// + /// [`State`]: struct.State.html + pub fn layout(&self) -> &Node { + &self.internal.layout + } + /// Returns the active [`Pane`] of the [`State`], if there is one. /// /// A [`Pane`] is active if it is focused and is __not__ being dragged. diff --git a/wgpu/src/widget/pane_grid.rs b/wgpu/src/widget/pane_grid.rs index 578e8960..54438f5c 100644 --- a/wgpu/src/widget/pane_grid.rs +++ b/wgpu/src/widget/pane_grid.rs @@ -11,8 +11,8 @@ use crate::Renderer; pub use iced_native::pane_grid::{ - Axis, Direction, DragEvent, Focus, KeyPressEvent, Pane, ResizeEvent, Split, - State, + Axis, Direction, DragEvent, Focus, KeyPressEvent, Node, Pane, ResizeEvent, + Split, State, }; /// A collection of panes distributed using either vertical or horizontal splits From c620e4dc4caaedf3ce74b14d95c1269eb7b1d535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 9 May 2020 00:28:31 +0200 Subject: [PATCH 3/6] Implement `State::get` in `pane_grid` --- native/src/widget/pane_grid/state.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs index 5cb41984..cca8fd4a 100644 --- a/native/src/widget/pane_grid/state.rs +++ b/native/src/widget/pane_grid/state.rs @@ -82,6 +82,14 @@ impl State { /// Returns the internal state of the given [`Pane`], if it exists. /// /// [`Pane`]: struct.Pane.html + pub fn get(&self, pane: &Pane) -> Option<&T> { + self.panes.get(pane) + } + + /// Returns the internal state of the given [`Pane`] with mutability, if it + /// exists. + /// + /// [`Pane`]: struct.Pane.html pub fn get_mut(&mut self, pane: &Pane) -> Option<&mut T> { self.panes.get_mut(pane) } From 32b9c1fdbd217c664aae40f5852013e682d480df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 9 May 2020 09:40:31 +0200 Subject: [PATCH 4/6] Return new `Split` in `pane_grid::State::split` --- native/src/widget/pane_grid/state.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs index cca8fd4a..4cda818c 100644 --- a/native/src/widget/pane_grid/state.rs +++ b/native/src/widget/pane_grid/state.rs @@ -191,7 +191,12 @@ impl State { /// /// [`Pane`]: struct.Pane.html /// [`Axis`]: enum.Axis.html - pub fn split(&mut self, axis: Axis, pane: &Pane, state: T) -> Option { + pub fn split( + &mut self, + axis: Axis, + pane: &Pane, + state: T, + ) -> Option<(Pane, Split)> { let node = self.internal.layout.find(pane)?; let new_pane = { @@ -211,7 +216,7 @@ impl State { let _ = self.panes.insert(new_pane, state); self.focus(&new_pane); - Some(new_pane) + Some((new_pane, new_split)) } /// Swaps the position of the provided panes in the [`State`]. From 2ab7341fa50865d6f0c26da59f945321ef839c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 12 May 2020 10:09:30 +0200 Subject: [PATCH 5/6] Implement `State::with_content` in `pane_grid` --- native/src/widget/pane_grid.rs | 2 + native/src/widget/pane_grid/content.rs | 12 +++++ native/src/widget/pane_grid/state.rs | 61 +++++++++++++++++++------- wgpu/src/widget/pane_grid.rs | 4 +- 4 files changed, 62 insertions(+), 17 deletions(-) create mode 100644 native/src/widget/pane_grid/content.rs diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 4c0eeed2..076ae76f 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -9,6 +9,7 @@ //! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.1/examples/pane_grid //! [`PaneGrid`]: struct.PaneGrid.html mod axis; +mod content; mod direction; mod node; mod pane; @@ -16,6 +17,7 @@ mod split; mod state; pub use axis::Axis; +pub use content::Content; pub use direction::Direction; pub use node::Node; pub use pane::Pane; diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs new file mode 100644 index 00000000..6b0bd99a --- /dev/null +++ b/native/src/widget/pane_grid/content.rs @@ -0,0 +1,12 @@ +use crate::pane_grid::Axis; + +#[derive(Debug, Clone)] +pub enum Content { + Split { + axis: Axis, + ratio: f32, + a: Box>, + b: Box>, + }, + Pane(T), +} diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs index 4cda818c..41f3cffd 100644 --- a/native/src/widget/pane_grid/state.rs +++ b/native/src/widget/pane_grid/state.rs @@ -1,6 +1,6 @@ use crate::{ keyboard, - pane_grid::{node::Node, Axis, Direction, Pane, Split}, + pane_grid::{Axis, Content, Direction, Node, Pane, Split}, Hasher, Point, Rectangle, Size, }; @@ -53,23 +53,24 @@ impl State { /// [`State`]: struct.State.html /// [`Pane`]: struct.Pane.html pub fn new(first_pane_state: T) -> (Self, Pane) { - let first_pane = Pane(0); + (Self::with_content(Content::Pane(first_pane_state)), Pane(0)) + } + pub fn with_content(content: impl Into>) -> Self { let mut panes = HashMap::new(); - let _ = panes.insert(first_pane, first_pane_state); - ( - State { - panes, - internal: Internal { - layout: Node::Pane(first_pane), - last_id: 0, - action: Action::Idle { focus: None }, - }, - modifiers: keyboard::ModifiersState::default(), + let (layout, last_id) = + Self::distribute_content(&mut panes, content.into(), 0); + + State { + panes, + internal: Internal { + layout, + last_id, + action: Action::Idle { focus: None }, }, - first_pane, - ) + modifiers: keyboard::ModifiersState::default(), + } } /// Returns the total amount of panes in the [`State`]. @@ -110,7 +111,7 @@ impl State { self.panes.iter_mut() } - /// Returns the layout tree stored in the [`State`]. + /// Returns the layout of the [`State`]. /// /// [`State`]: struct.State.html pub fn layout(&self) -> &Node { @@ -266,6 +267,36 @@ impl State { None } } + + fn distribute_content( + panes: &mut HashMap, + content: Content, + next_id: usize, + ) -> (Node, usize) { + match content { + Content::Split { axis, ratio, a, b } => { + let (a, next_id) = Self::distribute_content(panes, *a, next_id); + let (b, next_id) = Self::distribute_content(panes, *b, next_id); + + ( + Node::Split { + id: Split(next_id), + axis, + ratio, + a: Box::new(a), + b: Box::new(b), + }, + next_id + 1, + ) + } + Content::Pane(state) => { + let id = Pane(next_id); + let _ = panes.insert(id, state); + + (Node::Pane(id), next_id + 1) + } + } + } } #[derive(Debug, Clone)] diff --git a/wgpu/src/widget/pane_grid.rs b/wgpu/src/widget/pane_grid.rs index 54438f5c..6f437df7 100644 --- a/wgpu/src/widget/pane_grid.rs +++ b/wgpu/src/widget/pane_grid.rs @@ -11,8 +11,8 @@ use crate::Renderer; pub use iced_native::pane_grid::{ - Axis, Direction, DragEvent, Focus, KeyPressEvent, Node, Pane, ResizeEvent, - Split, State, + Axis, Content, Direction, DragEvent, Focus, KeyPressEvent, Node, Pane, + ResizeEvent, Split, State, }; /// A collection of panes distributed using either vertical or horizontal splits From 230bd6f7475752352ca9985802db0633e3508d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 22 May 2020 22:15:44 +0200 Subject: [PATCH 6/6] Write documentation for new `pane_grid` API --- native/src/widget/pane_grid/content.rs | 18 +++++++++++++ native/src/widget/pane_grid/node.rs | 36 ++++++++++++++++++++++++++ native/src/widget/pane_grid/state.rs | 4 +++ 3 files changed, 58 insertions(+) diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs index 6b0bd99a..8822083e 100644 --- a/native/src/widget/pane_grid/content.rs +++ b/native/src/widget/pane_grid/content.rs @@ -1,12 +1,30 @@ use crate::pane_grid::Axis; +/// The content of a [`PaneGrid`]. +/// +/// [`PaneGrid`]: struct.PaneGrid.html #[derive(Debug, Clone)] pub enum Content { + /// A split of the available space. Split { + /// The direction of the split. axis: Axis, + + /// The ratio of the split in [0.0, 1.0]. ratio: f32, + + /// The left/top [`Content`] of the split. + /// + /// [`Content`]: enum.Node.html a: Box>, + + /// The right/bottom [`Content`] of the split. + /// + /// [`Content`]: enum.Node.html b: Box>, }, + /// A [`Pane`]. + /// + /// [`Pane`]: struct.Pane.html Pane(T), } diff --git a/native/src/widget/pane_grid/node.rs b/native/src/widget/pane_grid/node.rs index 1b6633fa..723ec393 100644 --- a/native/src/widget/pane_grid/node.rs +++ b/native/src/widget/pane_grid/node.rs @@ -5,19 +5,49 @@ use crate::{ use std::collections::HashMap; +/// A layout node of a [`PaneGrid`]. +/// +/// [`PaneGrid`]: struct.PaneGrid.html #[derive(Debug, Clone)] pub enum Node { + /// The region of this [`Node`] is split into two. + /// + /// [`Node`]: enum.Node.html Split { + /// The [`Split`] of this [`Node`]. + /// + /// [`Split`]: struct.Split.html + /// [`Node`]: enum.Node.html id: Split, + + /// The direction of the split. axis: Axis, + + /// The ratio of the split in [0.0, 1.0]. ratio: f32, + + /// The left/top [`Node`] of the split. + /// + /// [`Node`]: enum.Node.html a: Box, + + /// The right/bottom [`Node`] of the split. + /// + /// [`Node`]: enum.Node.html b: Box, }, + /// The region of this [`Node`] is taken by a [`Pane`]. + /// + /// [`Pane`]: struct.Pane.html Pane(Pane), } impl Node { + /// Returns the rectangular region for each [`Pane`] in the [`Node`] given + /// the spacing between panes and the total available space. + /// + /// [`Pane`]: struct.Pane.html + /// [`Node`]: enum.Node.html pub fn regions( &self, spacing: f32, @@ -39,6 +69,12 @@ impl Node { regions } + /// Returns the axis, rectangular region, and ratio for each [`Split`] in + /// the [`Node`] given the spacing between panes and the total available + /// space. + /// + /// [`Split`]: struct.Split.html + /// [`Node`]: enum.Node.html pub fn splits( &self, spacing: f32, diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs index 41f3cffd..4b13fb8e 100644 --- a/native/src/widget/pane_grid/state.rs +++ b/native/src/widget/pane_grid/state.rs @@ -56,6 +56,10 @@ impl State { (Self::with_content(Content::Pane(first_pane_state)), Pane(0)) } + /// Creates a new [`State`] with the given [`Content`]. + /// + /// [`State`]: struct.State.html + /// [`Content`]: enum.Content.html pub fn with_content(content: impl Into>) -> Self { let mut panes = HashMap::new();