From 415fd4f643d385dad39bd0ee5a47de384643dcf3 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 4 Oct 2024 11:27:02 -0700 Subject: Use BTreeMap for Ord iteration of panes This ensures continuity in how panes are iterated on when building widget state --- widget/src/pane_grid/state.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'widget/src') diff --git a/widget/src/pane_grid/state.rs b/widget/src/pane_grid/state.rs index c20c3b9c..f5d2bb02 100644 --- a/widget/src/pane_grid/state.rs +++ b/widget/src/pane_grid/state.rs @@ -6,7 +6,7 @@ use crate::pane_grid::{ Axis, Configuration, Direction, Edge, Node, Pane, Region, Split, Target, }; -use rustc_hash::FxHashMap; +use std::collections::BTreeMap; /// The state of a [`PaneGrid`]. /// @@ -25,7 +25,7 @@ pub struct State { /// The panes of the [`PaneGrid`]. /// /// [`PaneGrid`]: super::PaneGrid - pub panes: FxHashMap, + pub panes: BTreeMap, /// The internal state of the [`PaneGrid`]. /// @@ -52,7 +52,7 @@ impl State { /// Creates a new [`State`] with the given [`Configuration`]. pub fn with_configuration(config: impl Into>) -> Self { - let mut panes = FxHashMap::default(); + let mut panes = BTreeMap::default(); let internal = Internal::from_configuration(&mut panes, config.into(), 0); @@ -353,7 +353,7 @@ impl Internal { /// /// [`PaneGrid`]: super::PaneGrid pub fn from_configuration( - panes: &mut FxHashMap, + panes: &mut BTreeMap, content: Configuration, next_id: usize, ) -> Self { -- cgit From 9ac3318357d636cde22ae34f7b2cdeddd3f55cdb Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 4 Oct 2024 11:31:14 -0700 Subject: Retain widget state against incoming panes We can associate each state with a `Pane` and compare that against the new panes to remove states w/ respective panes which no longer exist. Because we always increment `Pane`, new states are always added to the end, so this retain + add new state approach will ensure continuity when panes are added & removed --- widget/src/pane_grid.rs | 246 ++++++++++++++++++++++-------------------- widget/src/pane_grid/state.rs | 14 +-- 2 files changed, 135 insertions(+), 125 deletions(-) (limited to 'widget/src') diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index e6fda660..2644986f 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -92,6 +92,8 @@ use crate::core::{ Pixels, Point, Rectangle, Shell, Size, Theme, Vector, Widget, }; +use std::borrow::Cow; + const DRAG_DEADBAND_DISTANCE: f32 = 10.0; const THICKNESS_RATIO: f32 = 25.0; @@ -157,7 +159,10 @@ pub struct PaneGrid< Theme: Catalog, Renderer: core::Renderer, { - contents: Contents<'a, Content<'a, Message, Theme, Renderer>>, + internal: &'a state::Internal, + panes: Vec, + contents: Vec>, + maximized: Option, width: Length, height: Length, spacing: f32, @@ -180,30 +185,21 @@ where state: &'a State, view: impl Fn(Pane, &'a T, bool) -> Content<'a, Message, Theme, Renderer>, ) -> Self { - let contents = if let Some((pane, pane_state)) = - state.maximized.and_then(|pane| { - state.panes.get(&pane).map(|pane_state| (pane, pane_state)) - }) { - Contents::Maximized( - pane, - view(pane, pane_state, true), - Node::Pane(pane), - ) - } else { - Contents::All( - state - .panes - .iter() - .map(|(pane, pane_state)| { - (*pane, view(*pane, pane_state, false)) - }) - .collect(), - &state.internal, - ) - }; + let panes = state.panes.keys().copied().collect(); + let contents = state + .panes + .iter() + .map(|(pane, pane_state)| match &state.maximized { + Some(p) if pane == p => view(*pane, pane_state, true), + _ => view(*pane, pane_state, false), + }) + .collect(); Self { + internal: &state.internal, + panes, contents, + maximized: state.maximized, width: Length::Fill, height: Length::Fill, spacing: 0.0, @@ -248,7 +244,9 @@ where where F: 'a + Fn(DragEvent) -> Message, { - self.on_drag = Some(Box::new(f)); + if self.maximized.is_none() { + self.on_drag = Some(Box::new(f)); + } self } @@ -265,7 +263,9 @@ where where F: 'a + Fn(ResizeEvent) -> Message, { - self.on_resize = Some((leeway.into().0, Box::new(f))); + if self.maximized.is_none() { + self.on_resize = Some((leeway.into().0, Box::new(f))); + } self } @@ -291,10 +291,17 @@ where } fn drag_enabled(&self) -> bool { - (!self.contents.is_maximized()) + (self.maximized.is_none()) .then(|| self.on_drag.is_some()) .unwrap_or_default() } + + fn node(&self) -> Cow<'_, Node> { + match self.maximized { + Some(pane) => Cow::Owned(Node::Pane(pane)), + None => Cow::Borrowed(&self.internal.layout), + } + } } impl<'a, Message, Theme, Renderer> Widget @@ -304,33 +311,48 @@ where Renderer: core::Renderer, { fn tag(&self) -> tree::Tag { - tree::Tag::of::() + tree::Tag::of::() } fn state(&self) -> tree::State { - tree::State::new(state::Action::Idle) + tree::State::new(state::Widget::default()) } fn children(&self) -> Vec { - self.contents - .iter() - .map(|(_, content)| content.state()) - .collect() + self.contents.iter().map(Content::state).collect() } fn diff(&self, tree: &mut Tree) { - match &self.contents { - Contents::All(contents, _) => tree.diff_children_custom( - contents, - |state, (_, content)| content.diff(state), - |(_, content)| content.state(), - ), - Contents::Maximized(_, content, _) => tree.diff_children_custom( - &[content], - |state, content| content.diff(state), - |content| content.state(), - ), - } + let state::Widget { panes, .. } = tree.state.downcast_ref(); + + // `Pane` always increments and is iterated by Ord so new + // states are always added at the end. We can simply remove + // states which no longer exist and `diff_children` will + // diff the remaining values in the correct order and + // add new states at the end + + let mut i = 0; + let mut j = 0; + tree.children.retain(|_| { + let retain = self.panes.get(i) == panes.get(j); + + if retain { + i += 1; + } + j += 1; + + retain + }); + + tree.diff_children_custom( + &self.contents, + |state, content| content.diff(state), + Content::state, + ); + + let state::Widget { panes, .. } = tree.state.downcast_mut(); + + panes.clone_from(&self.panes); } fn size(&self) -> Size { @@ -347,14 +369,19 @@ where limits: &layout::Limits, ) -> layout::Node { let size = limits.resolve(self.width, self.height, Size::ZERO); - let node = self.contents.layout(); - let regions = node.pane_regions(self.spacing, size); + let regions = self.node().pane_regions(self.spacing, size); let children = self - .contents + .panes .iter() + .copied() + .zip(&self.contents) .zip(tree.children.iter_mut()) .filter_map(|((pane, content), tree)| { + if self.maximized.is_some() && Some(pane) != self.maximized { + return Some(layout::Node::new(Size::ZERO)); + } + let region = regions.get(&pane)?; let size = Size::new(region.width, region.height); @@ -379,11 +406,16 @@ where operation: &mut dyn widget::Operation, ) { operation.container(None, layout.bounds(), &mut |operation| { - self.contents + self.panes .iter() + .copied() + .zip(&self.contents) .zip(&mut tree.children) .zip(layout.children()) - .for_each(|(((_pane, content), state), layout)| { + .filter(|(((pane, _), _), _)| { + self.maximized.map_or(true, |maximized| *pane == maximized) + }) + .for_each(|(((_, content), state), layout)| { content.operate(state, layout, renderer, operation); }); }); @@ -402,8 +434,8 @@ where ) -> event::Status { let mut event_status = event::Status::Ignored; - let action = tree.state.downcast_mut::(); - let node = self.contents.layout(); + let state::Widget { action, .. } = tree.state.downcast_mut(); + let node = self.node(); let on_drag = if self.drag_enabled() { &self.on_drag @@ -448,7 +480,10 @@ where layout, cursor_position, shell, - self.contents.iter(), + self.panes + .iter() + .copied() + .zip(&self.contents), &self.on_click, on_drag, ); @@ -460,7 +495,7 @@ where layout, cursor_position, shell, - self.contents.iter(), + self.panes.iter().copied().zip(&self.contents), &self.on_click, on_drag, ); @@ -486,8 +521,10 @@ where } } else { let dropped_region = self - .contents + .panes .iter() + .copied() + .zip(&self.contents) .zip(layout.children()) .find_map(|(target, layout)| { layout_region( @@ -572,10 +609,15 @@ where let picked_pane = action.picked_pane().map(|(pane, _)| pane); - self.contents - .iter_mut() + self.panes + .iter() + .copied() + .zip(&mut self.contents) .zip(&mut tree.children) .zip(layout.children()) + .filter(|(((pane, _), _), _)| { + self.maximized.map_or(true, |maximized| *pane == maximized) + }) .map(|(((pane, content), tree), layout)| { let is_picked = picked_pane == Some(pane); @@ -602,14 +644,14 @@ where viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { - let action = tree.state.downcast_ref::(); + let state::Widget { action, .. } = tree.state.downcast_ref(); if action.picked_pane().is_some() { return mouse::Interaction::Grabbing; } let resize_leeway = self.on_resize.as_ref().map(|(leeway, _)| *leeway); - let node = self.contents.layout(); + let node = self.node(); let resize_axis = action.picked_split().map(|(_, axis)| axis).or_else(|| { @@ -641,11 +683,16 @@ where }; } - self.contents + self.panes .iter() + .copied() + .zip(&self.contents) .zip(&tree.children) .zip(layout.children()) - .map(|(((_pane, content), tree), layout)| { + .filter(|(((pane, _), _), _)| { + self.maximized.map_or(true, |maximized| *pane == maximized) + }) + .map(|(((_, content), tree), layout)| { content.mouse_interaction( tree, layout, @@ -669,16 +716,11 @@ where cursor: mouse::Cursor, viewport: &Rectangle, ) { - let action = tree.state.downcast_ref::(); - let node = self.contents.layout(); + let state::Widget { action, .. } = + tree.state.downcast_ref::(); + let node = self.node(); let resize_leeway = self.on_resize.as_ref().map(|(leeway, _)| *leeway); - let contents = self - .contents - .iter() - .zip(&tree.children) - .map(|((pane, content), tree)| (pane, (content, tree))); - let picked_pane = action.picked_pane().filter(|(_, origin)| { cursor .position() @@ -747,8 +789,16 @@ where let style = Catalog::style(theme, &self.class); - for ((id, (content, tree)), pane_layout) in - contents.zip(layout.children()) + for (((id, content), tree), pane_layout) in self + .panes + .iter() + .copied() + .zip(&self.contents) + .zip(&tree.children) + .zip(layout.children()) + .filter(|(((pane, _), _), _)| { + self.maximized.map_or(true, |maximized| maximized == *pane) + }) { match picked_pane { Some((dragging, origin)) if id == dragging => { @@ -883,11 +933,17 @@ where translation: Vector, ) -> Option> { let children = self - .contents - .iter_mut() + .panes + .iter() + .copied() + .zip(&mut self.contents) .zip(&mut tree.children) .zip(layout.children()) - .filter_map(|(((_, content), state), layout)| { + .filter_map(|(((pane, content), state), layout)| { + if self.maximized.is_some() && Some(pane) != self.maximized { + return None; + } + content.overlay(state, layout, renderer, translation) }) .collect::>(); @@ -1136,52 +1192,6 @@ fn hovered_split<'a>( }) } -/// The visible contents of the [`PaneGrid`] -#[derive(Debug)] -pub enum Contents<'a, T> { - /// All panes are visible - All(Vec<(Pane, T)>, &'a state::Internal), - /// A maximized pane is visible - Maximized(Pane, T, Node), -} - -impl<'a, T> Contents<'a, T> { - /// Returns the layout [`Node`] of the [`Contents`] - pub fn layout(&self) -> &Node { - match self { - Contents::All(_, state) => state.layout(), - Contents::Maximized(_, _, layout) => layout, - } - } - - /// Returns an iterator over the values of the [`Contents`] - pub fn iter(&self) -> Box + '_> { - match self { - Contents::All(contents, _) => Box::new( - contents.iter().map(|(pane, content)| (*pane, content)), - ), - Contents::Maximized(pane, content, _) => { - Box::new(std::iter::once((*pane, content))) - } - } - } - - fn iter_mut(&mut self) -> Box + '_> { - match self { - Contents::All(contents, _) => Box::new( - contents.iter_mut().map(|(pane, content)| (*pane, content)), - ), - Contents::Maximized(pane, content, _) => { - Box::new(std::iter::once((*pane, content))) - } - } - } - - fn is_maximized(&self) -> bool { - matches!(self, Self::Maximized(..)) - } -} - /// The appearance of a [`PaneGrid`]. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Style { diff --git a/widget/src/pane_grid/state.rs b/widget/src/pane_grid/state.rs index f5d2bb02..e1934930 100644 --- a/widget/src/pane_grid/state.rs +++ b/widget/src/pane_grid/state.rs @@ -343,7 +343,7 @@ impl State { /// [`PaneGrid`]: super::PaneGrid #[derive(Debug, Clone)] pub struct Internal { - layout: Node, + pub(super) layout: Node, last_id: usize, } @@ -397,11 +397,12 @@ impl Internal { /// The current action of a [`PaneGrid`]. /// /// [`PaneGrid`]: super::PaneGrid -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Default)] pub enum Action { /// The [`PaneGrid`] is idle. /// /// [`PaneGrid`]: super::PaneGrid + #[default] Idle, /// A [`Pane`] in the [`PaneGrid`] is being dragged. /// @@ -441,9 +442,8 @@ impl Action { } } -impl Internal { - /// The layout [`Node`] of the [`Internal`] state - pub fn layout(&self) -> &Node { - &self.layout - } +#[derive(Default)] +pub(super) struct Widget { + pub action: Action, + pub panes: Vec, } -- cgit From 5ebd8ac83f6c173bd24de146bf582f049663a330 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 4 Oct 2024 11:34:14 -0700 Subject: Keep `Pane` associated to state / layout after swap State continuity is dependent on keeping a node associated to it's original `Pane` id. When splitting -> swapping nodes, we need to assign it back to the original `Pane` to enforce continuity. --- widget/src/pane_grid/state.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'widget/src') diff --git a/widget/src/pane_grid/state.rs b/widget/src/pane_grid/state.rs index e1934930..b7aef67d 100644 --- a/widget/src/pane_grid/state.rs +++ b/widget/src/pane_grid/state.rs @@ -228,8 +228,15 @@ impl State { ) { if let Some((state, _)) = self.close(pane) { if let Some((new_pane, _)) = self.split(axis, target, state) { + // Ensure new node corresponds to original `Pane` for state continuity + self.swap(pane, new_pane); + let _ = self + .panes + .remove(&new_pane) + .and_then(|state| self.panes.insert(pane, state)); + if swap { - self.swap(target, new_pane); + self.swap(target, pane); } } } @@ -262,7 +269,16 @@ impl State { swap: bool, ) { if let Some((state, _)) = self.close(pane) { - let _ = self.split_node(axis, None, state, swap); + if let Some((new_pane, _)) = + self.split_node(axis, None, state, swap) + { + // Ensure new node corresponds to original `Pane` for state continuity + self.swap(pane, new_pane); + let _ = self + .panes + .remove(&new_pane) + .and_then(|state| self.panes.insert(pane, state)); + } } } -- cgit From 659669dd5810d470d51f528495cab2f676eb58ed Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 24 Oct 2024 13:47:28 +0200 Subject: Remove duplicated `maximized` state in `pane_grid` --- widget/src/pane_grid.rs | 60 ++++++++++++++++++++++++------------------- widget/src/pane_grid/state.rs | 44 ++++++++++++++++++------------- 2 files changed, 59 insertions(+), 45 deletions(-) (limited to 'widget/src') diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 2644986f..adda79dd 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -92,8 +92,6 @@ use crate::core::{ Pixels, Point, Rectangle, Shell, Size, Theme, Vector, Widget, }; -use std::borrow::Cow; - const DRAG_DEADBAND_DISTANCE: f32 = 10.0; const THICKNESS_RATIO: f32 = 25.0; @@ -162,7 +160,6 @@ pub struct PaneGrid< internal: &'a state::Internal, panes: Vec, contents: Vec>, - maximized: Option, width: Length, height: Length, spacing: f32, @@ -189,8 +186,8 @@ where let contents = state .panes .iter() - .map(|(pane, pane_state)| match &state.maximized { - Some(p) if pane == p => view(*pane, pane_state, true), + .map(|(pane, pane_state)| match state.maximized() { + Some(p) if *pane == p => view(*pane, pane_state, true), _ => view(*pane, pane_state, false), }) .collect(); @@ -199,7 +196,6 @@ where internal: &state.internal, panes, contents, - maximized: state.maximized, width: Length::Fill, height: Length::Fill, spacing: 0.0, @@ -244,7 +240,7 @@ where where F: 'a + Fn(DragEvent) -> Message, { - if self.maximized.is_none() { + if self.internal.maximized().is_none() { self.on_drag = Some(Box::new(f)); } self @@ -263,7 +259,7 @@ where where F: 'a + Fn(ResizeEvent) -> Message, { - if self.maximized.is_none() { + if self.internal.maximized().is_none() { self.on_resize = Some((leeway.into().0, Box::new(f))); } self @@ -291,17 +287,12 @@ where } fn drag_enabled(&self) -> bool { - (self.maximized.is_none()) + self.internal + .maximized() + .is_none() .then(|| self.on_drag.is_some()) .unwrap_or_default() } - - fn node(&self) -> Cow<'_, Node> { - match self.maximized { - Some(pane) => Cow::Owned(Node::Pane(pane)), - None => Cow::Borrowed(&self.internal.layout), - } - } } impl<'a, Message, Theme, Renderer> Widget @@ -351,7 +342,6 @@ where ); let state::Widget { panes, .. } = tree.state.downcast_mut(); - panes.clone_from(&self.panes); } @@ -369,7 +359,7 @@ where limits: &layout::Limits, ) -> layout::Node { let size = limits.resolve(self.width, self.height, Size::ZERO); - let regions = self.node().pane_regions(self.spacing, size); + let regions = self.internal.layout().pane_regions(self.spacing, size); let children = self .panes @@ -378,7 +368,11 @@ where .zip(&self.contents) .zip(tree.children.iter_mut()) .filter_map(|((pane, content), tree)| { - if self.maximized.is_some() && Some(pane) != self.maximized { + if self + .internal + .maximized() + .is_some_and(|maximized| maximized != pane) + { return Some(layout::Node::new(Size::ZERO)); } @@ -413,7 +407,9 @@ where .zip(&mut tree.children) .zip(layout.children()) .filter(|(((pane, _), _), _)| { - self.maximized.map_or(true, |maximized| *pane == maximized) + self.internal + .maximized() + .map_or(true, |maximized| *pane == maximized) }) .for_each(|(((_, content), state), layout)| { content.operate(state, layout, renderer, operation); @@ -435,7 +431,7 @@ where let mut event_status = event::Status::Ignored; let state::Widget { action, .. } = tree.state.downcast_mut(); - let node = self.node(); + let node = self.internal.layout(); let on_drag = if self.drag_enabled() { &self.on_drag @@ -616,7 +612,9 @@ where .zip(&mut tree.children) .zip(layout.children()) .filter(|(((pane, _), _), _)| { - self.maximized.map_or(true, |maximized| *pane == maximized) + self.internal + .maximized() + .map_or(true, |maximized| *pane == maximized) }) .map(|(((pane, content), tree), layout)| { let is_picked = picked_pane == Some(pane); @@ -651,7 +649,7 @@ where } let resize_leeway = self.on_resize.as_ref().map(|(leeway, _)| *leeway); - let node = self.node(); + let node = self.internal.layout(); let resize_axis = action.picked_split().map(|(_, axis)| axis).or_else(|| { @@ -690,7 +688,9 @@ where .zip(&tree.children) .zip(layout.children()) .filter(|(((pane, _), _), _)| { - self.maximized.map_or(true, |maximized| *pane == maximized) + self.internal + .maximized() + .map_or(true, |maximized| *pane == maximized) }) .map(|(((_, content), tree), layout)| { content.mouse_interaction( @@ -718,7 +718,7 @@ where ) { let state::Widget { action, .. } = tree.state.downcast_ref::(); - let node = self.node(); + let node = self.internal.layout(); let resize_leeway = self.on_resize.as_ref().map(|(leeway, _)| *leeway); let picked_pane = action.picked_pane().filter(|(_, origin)| { @@ -797,7 +797,9 @@ where .zip(&tree.children) .zip(layout.children()) .filter(|(((pane, _), _), _)| { - self.maximized.map_or(true, |maximized| maximized == *pane) + self.internal + .maximized() + .map_or(true, |maximized| maximized == *pane) }) { match picked_pane { @@ -940,7 +942,11 @@ where .zip(&mut tree.children) .zip(layout.children()) .filter_map(|(((pane, content), state), layout)| { - if self.maximized.is_some() && Some(pane) != self.maximized { + if self + .internal + .maximized() + .is_some_and(|maximized| maximized != pane) + { return None; } diff --git a/widget/src/pane_grid/state.rs b/widget/src/pane_grid/state.rs index b7aef67d..f7e8f750 100644 --- a/widget/src/pane_grid/state.rs +++ b/widget/src/pane_grid/state.rs @@ -6,6 +6,7 @@ use crate::pane_grid::{ Axis, Configuration, Direction, Edge, Node, Pane, Region, Split, Target, }; +use std::borrow::Cow; use std::collections::BTreeMap; /// The state of a [`PaneGrid`]. @@ -31,11 +32,6 @@ pub struct State { /// /// [`PaneGrid`]: super::PaneGrid pub internal: Internal, - - /// The maximized [`Pane`] of the [`PaneGrid`]. - /// - /// [`PaneGrid`]: super::PaneGrid - pub(super) maximized: Option, } impl State { @@ -57,11 +53,7 @@ impl State { let internal = Internal::from_configuration(&mut panes, config.into(), 0); - State { - panes, - internal, - maximized: None, - } + State { panes, internal } } /// Returns the total amount of panes in the [`State`]. @@ -214,7 +206,7 @@ impl State { } let _ = self.panes.insert(new_pane, state); - let _ = self.maximized.take(); + let _ = self.internal.maximized.take(); Some((new_pane, new_split)) } @@ -319,8 +311,8 @@ impl State { /// Closes the given [`Pane`] and returns its internal state and its closest /// sibling, if it exists. pub fn close(&mut self, pane: Pane) -> Option<(T, Pane)> { - if self.maximized == Some(pane) { - let _ = self.maximized.take(); + if self.internal.maximized == Some(pane) { + let _ = self.internal.maximized.take(); } if let Some(sibling) = self.internal.layout.remove(pane) { @@ -335,7 +327,7 @@ impl State { /// /// [`PaneGrid`]: super::PaneGrid pub fn maximize(&mut self, pane: Pane) { - self.maximized = Some(pane); + self.internal.maximized = Some(pane); } /// Restore the currently maximized [`Pane`] to it's normal size. All panes @@ -343,14 +335,14 @@ impl State { /// /// [`PaneGrid`]: super::PaneGrid pub fn restore(&mut self) { - let _ = self.maximized.take(); + let _ = self.internal.maximized.take(); } /// Returns the maximized [`Pane`] of the [`PaneGrid`]. /// /// [`PaneGrid`]: super::PaneGrid pub fn maximized(&self) -> Option { - self.maximized + self.internal.maximized } } @@ -359,8 +351,9 @@ impl State { /// [`PaneGrid`]: super::PaneGrid #[derive(Debug, Clone)] pub struct Internal { - pub(super) layout: Node, + layout: Node, last_id: usize, + maximized: Option, } impl Internal { @@ -406,7 +399,22 @@ impl Internal { } }; - Self { layout, last_id } + Self { + layout, + last_id, + maximized: None, + } + } + + pub(super) fn layout(&self) -> Cow<'_, Node> { + match self.maximized { + Some(pane) => Cow::Owned(Node::Pane(pane)), + None => Cow::Borrowed(&self.layout), + } + } + + pub(super) fn maximized(&self) -> Option { + self.maximized } } -- cgit From 089e629f4103bbd248c5f80441774d6ce98680fe Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 24 Oct 2024 13:48:42 +0200 Subject: Fix `responsive` diffing when `Tree` is emptied by ancestors --- widget/src/lazy/responsive.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs index a7a99f56..a6c40ab0 100644 --- a/widget/src/lazy/responsive.rs +++ b/widget/src/lazy/responsive.rs @@ -83,7 +83,10 @@ where new_size: Size, view: &dyn Fn(Size) -> Element<'a, Message, Theme, Renderer>, ) { - if self.size == new_size { + let is_tree_empty = + tree.tag == tree::Tag::stateless() && tree.children.is_empty(); + + if !is_tree_empty && self.size == new_size { return; } -- cgit From 55504ffcd41a5f1c8c35889501337a729b6aac28 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 24 Oct 2024 13:55:04 +0200 Subject: Rename `state::Widget` to `pane_grid::Memory` --- widget/src/pane_grid.rs | 25 +++++++++++++++---------- widget/src/pane_grid/state.rs | 6 ------ 2 files changed, 15 insertions(+), 16 deletions(-) (limited to 'widget/src') diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index adda79dd..b4ed4b64 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -295,6 +295,12 @@ where } } +#[derive(Default)] +struct Memory { + action: state::Action, + order: Vec, +} + impl<'a, Message, Theme, Renderer> Widget for PaneGrid<'a, Message, Theme, Renderer> where @@ -302,11 +308,11 @@ where Renderer: core::Renderer, { fn tag(&self) -> tree::Tag { - tree::Tag::of::() + tree::Tag::of::() } fn state(&self) -> tree::State { - tree::State::new(state::Widget::default()) + tree::State::new(Memory::default()) } fn children(&self) -> Vec { @@ -314,7 +320,7 @@ where } fn diff(&self, tree: &mut Tree) { - let state::Widget { panes, .. } = tree.state.downcast_ref(); + let Memory { order, .. } = tree.state.downcast_ref(); // `Pane` always increments and is iterated by Ord so new // states are always added at the end. We can simply remove @@ -325,7 +331,7 @@ where let mut i = 0; let mut j = 0; tree.children.retain(|_| { - let retain = self.panes.get(i) == panes.get(j); + let retain = self.panes.get(i) == order.get(j); if retain { i += 1; @@ -341,8 +347,8 @@ where Content::state, ); - let state::Widget { panes, .. } = tree.state.downcast_mut(); - panes.clone_from(&self.panes); + let Memory { order, .. } = tree.state.downcast_mut(); + order.clone_from(&self.panes); } fn size(&self) -> Size { @@ -430,7 +436,7 @@ where ) -> event::Status { let mut event_status = event::Status::Ignored; - let state::Widget { action, .. } = tree.state.downcast_mut(); + let Memory { action, .. } = tree.state.downcast_mut(); let node = self.internal.layout(); let on_drag = if self.drag_enabled() { @@ -642,7 +648,7 @@ where viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { - let state::Widget { action, .. } = tree.state.downcast_ref(); + let Memory { action, .. } = tree.state.downcast_ref(); if action.picked_pane().is_some() { return mouse::Interaction::Grabbing; @@ -716,8 +722,7 @@ where cursor: mouse::Cursor, viewport: &Rectangle, ) { - let state::Widget { action, .. } = - tree.state.downcast_ref::(); + let Memory { action, .. } = tree.state.downcast_ref(); let node = self.internal.layout(); let resize_leeway = self.on_resize.as_ref().map(|(leeway, _)| *leeway); diff --git a/widget/src/pane_grid/state.rs b/widget/src/pane_grid/state.rs index f7e8f750..67cd3bf2 100644 --- a/widget/src/pane_grid/state.rs +++ b/widget/src/pane_grid/state.rs @@ -465,9 +465,3 @@ impl Action { } } } - -#[derive(Default)] -pub(super) struct Widget { - pub action: Action, - pub panes: Vec, -} -- cgit From d08bc6e45d12c7a5e4037c20673ff832eb7802ec Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 24 Oct 2024 14:17:38 +0200 Subject: Add `relabel` helper to `pane_grid::State` --- widget/src/pane_grid/state.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'widget/src') diff --git a/widget/src/pane_grid/state.rs b/widget/src/pane_grid/state.rs index 67cd3bf2..2f8a64ea 100644 --- a/widget/src/pane_grid/state.rs +++ b/widget/src/pane_grid/state.rs @@ -220,12 +220,8 @@ impl State { ) { if let Some((state, _)) = self.close(pane) { if let Some((new_pane, _)) = self.split(axis, target, state) { - // Ensure new node corresponds to original `Pane` for state continuity - self.swap(pane, new_pane); - let _ = self - .panes - .remove(&new_pane) - .and_then(|state| self.panes.insert(pane, state)); + // Ensure new node corresponds to original closed `Pane` for state continuity + self.relabel(new_pane, pane); if swap { self.swap(target, pane); @@ -258,22 +254,27 @@ impl State { &mut self, axis: Axis, pane: Pane, - swap: bool, + inverse: bool, ) { if let Some((state, _)) = self.close(pane) { if let Some((new_pane, _)) = - self.split_node(axis, None, state, swap) + self.split_node(axis, None, state, inverse) { - // Ensure new node corresponds to original `Pane` for state continuity - self.swap(pane, new_pane); - let _ = self - .panes - .remove(&new_pane) - .and_then(|state| self.panes.insert(pane, state)); + // Ensure new node corresponds to original closed `Pane` for state continuity + self.relabel(new_pane, pane); } } } + fn relabel(&mut self, target: Pane, label: Pane) { + self.swap(target, label); + + let _ = self + .panes + .remove(&target) + .and_then(|state| self.panes.insert(label, state)); + } + /// Swaps the position of the provided panes in the [`State`]. /// /// If you want to swap panes on drag and drop in your [`PaneGrid`], you -- cgit