From b761ab5e1d4ceaae6ac12c28f45dfcd84c76c329 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Wed, 2 Nov 2022 16:49:18 -0700 Subject: [PATCH 1/9] Add maximize / restore to PaneGrid --- native/src/widget/pane_grid.rs | 129 ++++++++++++++++++++------- native/src/widget/pane_grid/state.rs | 54 ++++++----- 2 files changed, 128 insertions(+), 55 deletions(-) diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 96cf78efa2..1b55537f70 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -101,7 +101,7 @@ where Renderer::Theme: StyleSheet + container::StyleSheet, { state: &'a state::Internal, - elements: Vec<(Pane, Content<'a, Message, Renderer>)>, + elements: Elements>, width: Length, height: Length, spacing: u16, @@ -119,17 +119,30 @@ where /// Creates a [`PaneGrid`] with the given [`State`] and view function. /// /// The view function will be called to display each [`Pane`] present in the - /// [`State`]. + /// [`State`]. [`bool`] is set if the pane is maximized. pub fn new( state: &'a State, - view: impl Fn(Pane, &'a T) -> Content<'a, Message, Renderer>, + view: impl Fn(Pane, &'a T, bool) -> Content<'a, Message, Renderer>, ) -> Self { - let elements = { - state - .panes - .iter() - .map(|(pane, pane_state)| (*pane, view(*pane, pane_state))) - .collect() + let elements = if let Some((pane, pane_state)) = + state.maximized.and_then(|pane| { + state.panes.get(&pane).map(|pane_state| (pane, pane_state)) + }) { + Elements::Maximized( + pane, + view(pane, pane_state, true), + Node::Pane(pane), + ) + } else { + Elements::Normal( + state + .panes + .iter() + .map(|(pane, pane_state)| { + (*pane, view(*pane, pane_state, false)) + }) + .collect(), + ) }; Self { @@ -232,11 +245,18 @@ where } fn diff(&self, tree: &mut Tree) { - tree.diff_children_custom( - &self.elements, - |state, (_, content)| content.diff(state), - |(_, content)| content.state(), - ) + match &self.elements { + Elements::Normal(elements) => tree.diff_children_custom( + elements, + |state, (_, content)| content.diff(state), + |(_, content)| content.state(), + ), + Elements::Maximized(_, content, _) => tree.diff_children_custom( + &[content], + |state, content| content.diff(state), + |content| content.state(), + ), + } } fn width(&self) -> Length { @@ -255,11 +275,11 @@ where layout( renderer, limits, - self.state, + self.elements.node(self.state), self.width, self.height, self.spacing, - self.elements.iter().map(|(pane, content)| (*pane, content)), + self.elements.iter(), |element, renderer, limits| element.layout(renderer, limits), ) } @@ -278,13 +298,13 @@ where let event_status = update( action, - self.state, + self.elements.node(self.state), &event, layout, cursor_position, shell, self.spacing, - self.elements.iter().map(|(pane, content)| (*pane, content)), + self.elements.iter(), &self.on_click, &self.on_drag, &self.on_resize, @@ -297,7 +317,7 @@ where .zip(&mut tree.children) .zip(layout.children()) .map(|(((pane, content), tree), layout)| { - let is_picked = picked_pane == Some(*pane); + let is_picked = picked_pane == Some(pane); content.on_event( tree, @@ -323,7 +343,7 @@ where ) -> mouse::Interaction { mouse_interaction( tree.state.downcast_ref(), - self.state, + self.elements.node(self.state), layout, cursor_position, self.spacing, @@ -361,7 +381,7 @@ where ) { draw( tree.state.downcast_ref(), - self.state, + self.elements.node(self.state), layout, cursor_position, renderer, @@ -374,7 +394,7 @@ where self.elements .iter() .zip(&tree.children) - .map(|((pane, content), tree)| (*pane, (content, tree))), + .map(|((pane, content), tree)| (pane, (content, tree))), |(content, tree), renderer, style, @@ -429,7 +449,7 @@ where pub fn layout( renderer: &Renderer, limits: &layout::Limits, - state: &state::Internal, + node: &Node, width: Length, height: Length, spacing: u16, @@ -439,7 +459,7 @@ pub fn layout( let limits = limits.width(width).height(height); let size = limits.resolve(Size::ZERO); - let regions = state.pane_regions(f32::from(spacing), size); + let regions = node.pane_regions(f32::from(spacing), size); let children = elements .filter_map(|(pane, element)| { let region = regions.get(&pane)?; @@ -464,7 +484,7 @@ pub fn layout( /// accordingly. pub fn update<'a, Message, T: Draggable>( action: &mut state::Action, - state: &state::Internal, + node: &Node, event: &Event, layout: Layout<'_>, cursor_position: Point, @@ -492,7 +512,7 @@ pub fn update<'a, Message, T: Draggable>( cursor_position.y - bounds.y, ); - let splits = state.split_regions( + let splits = node.split_regions( f32::from(spacing), Size::new(bounds.width, bounds.height), ); @@ -570,7 +590,7 @@ pub fn update<'a, Message, T: Draggable>( if let Some((split, _)) = action.picked_split() { let bounds = layout.bounds(); - let splits = state.split_regions( + let splits = node.split_regions( f32::from(spacing), Size::new(bounds.width, bounds.height), ); @@ -642,7 +662,7 @@ fn click_pane<'a, Message, T>( /// Returns the current [`mouse::Interaction`] of a [`PaneGrid`]. pub fn mouse_interaction( action: &state::Action, - state: &state::Internal, + node: &Node, layout: Layout<'_>, cursor_position: Point, spacing: u16, @@ -658,7 +678,7 @@ pub fn mouse_interaction( let bounds = layout.bounds(); let splits = - state.split_regions(f32::from(spacing), bounds.size()); + node.split_regions(f32::from(spacing), bounds.size()); let relative_cursor = Point::new( cursor_position.x - bounds.x, @@ -687,7 +707,7 @@ pub fn mouse_interaction( /// Draws a [`PaneGrid`]. pub fn draw( action: &state::Action, - state: &state::Internal, + node: &Node, layout: Layout<'_>, cursor_position: Point, renderer: &mut Renderer, @@ -717,7 +737,7 @@ pub fn draw( .and_then(|(split, axis)| { let bounds = layout.bounds(); - let splits = state.split_regions(f32::from(spacing), bounds.size()); + let splits = node.split_regions(f32::from(spacing), bounds.size()); let (_axis, region, ratio) = splits.get(&split)?; @@ -736,7 +756,7 @@ pub fn draw( ); let splits = - state.split_regions(f32::from(spacing), bounds.size()); + node.split_regions(f32::from(spacing), bounds.size()); let (_split, axis, region) = hovered_split( splits.iter(), @@ -897,3 +917,48 @@ fn hovered_split<'a>( }) .next() } + +/// TODO +#[derive(Debug)] +pub enum Elements { + /// TODO + Normal(Vec<(Pane, T)>), + /// TODO + Maximized(Pane, T, Node), +} + +impl Elements { + /// TODO + pub fn iter(&self) -> Box + '_> { + match self { + Elements::Normal(elements) => Box::new( + elements.iter().map(|(pane, content)| (*pane, content)), + ), + Elements::Maximized(pane, content, _) => { + Box::new(std::iter::once((*pane, content))) + } + } + } + + /// TODO + pub fn iter_mut( + &mut self, + ) -> Box + '_> { + match self { + Elements::Normal(elements) => Box::new( + elements.iter_mut().map(|(pane, content)| (*pane, content)), + ), + Elements::Maximized(pane, content, _) => { + Box::new(std::iter::once((*pane, content))) + } + } + } + + /// TODO + pub fn node<'a>(&'a self, state: &'a state::Internal) -> &'a Node { + match self { + Elements::Normal(_) => state.layout(), + Elements::Maximized(_, _, node) => node, + } + } +} diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs index cdca6267d9..92d26f5a16 100644 --- a/native/src/widget/pane_grid/state.rs +++ b/native/src/widget/pane_grid/state.rs @@ -4,9 +4,9 @@ use crate::widget::pane_grid::{ Axis, Configuration, Direction, Node, Pane, Split, }; -use crate::{Point, Rectangle, Size}; +use crate::{Point, Size}; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; /// The state of a [`PaneGrid`]. /// @@ -31,6 +31,9 @@ pub struct State { /// /// [`PaneGrid`]: crate::widget::PaneGrid pub internal: Internal, + + /// The maximized [`Pane`] of the [`PaneGrid`] + pub(super) maximized: Option, } impl State { @@ -52,7 +55,11 @@ impl State { let internal = Internal::from_configuration(&mut panes, config.into(), 0); - State { panes, internal } + State { + panes, + internal, + maximized: None, + } } /// Returns the total amount of panes in the [`State`]. @@ -194,12 +201,28 @@ 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 let Some(sibling) = self.internal.layout.remove(pane) { self.panes.remove(pane).map(|state| (state, sibling)) } else { None } } + + /// Maximize the given [`Pane`]. Only this pane will be rendered by the + /// [`PaneGrid`] until [`Self::restore()`] is called. + pub fn maximize(&mut self, pane: &Pane) { + self.maximized = Some(*pane); + } + + /// Restore the currently maximized [`Pane`] to it's normal size. All panes + /// will be rendered by the [`PaneGrid`] + pub fn restore(&mut self) { + let _ = self.maximized.take(); + } } /// The internal state of a [`PaneGrid`]. @@ -226,11 +249,13 @@ impl Internal { let Internal { layout: a, last_id: next_id, + .. } = Self::from_configuration(panes, *a, next_id); let Internal { layout: b, last_id: next_id, + .. } = Self::from_configuration(panes, *b, next_id); ( @@ -304,25 +329,8 @@ impl Action { } impl Internal { - /// Calculates the current [`Pane`] regions from the [`PaneGrid`] layout. - /// - /// [`PaneGrid`]: crate::widget::PaneGrid - pub fn pane_regions( - &self, - spacing: f32, - size: Size, - ) -> BTreeMap { - self.layout.pane_regions(spacing, size) - } - - /// Calculates the current [`Split`] regions from the [`PaneGrid`] layout. - /// - /// [`PaneGrid`]: crate::widget::PaneGrid - pub fn split_regions( - &self, - spacing: f32, - size: Size, - ) -> BTreeMap { - self.layout.split_regions(spacing, size) + /// The layout [`Node`] of the [`Internal`] state + pub fn layout(&self) -> &Node { + &self.layout } } From e28c441c693ce9f42d60c98ba6892c6fc78d6724 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Wed, 2 Nov 2022 17:05:58 -0700 Subject: [PATCH 2/9] Update pane_grid example w/ maximize / restore --- examples/pane_grid/src/main.rs | 40 +++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index ae8fa22b0c..c9f1376cc0 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -29,6 +29,8 @@ enum Message { Dragged(pane_grid::DragEvent), Resized(pane_grid::ResizeEvent), TogglePin(pane_grid::Pane), + Maximize(pane_grid::Pane), + Restore, Close(pane_grid::Pane), CloseFocused, } @@ -114,6 +116,10 @@ impl Application for Example { *is_pinned = !*is_pinned; } } + Message::Maximize(pane) => self.panes.maximize(&pane), + Message::Restore => { + self.panes.restore(); + } Message::Close(pane) => { if let Some((_, sibling)) = self.panes.close(&pane) { self.focus = Some(sibling); @@ -157,7 +163,7 @@ impl Application for Example { let focus = self.focus; let total_panes = self.panes.len(); - let pane_grid = PaneGrid::new(&self.panes, |id, pane| { + let pane_grid = PaneGrid::new(&self.panes, |id, pane, is_maximized| { let is_focused = focus == Some(id); let pin_button = button( @@ -178,7 +184,12 @@ impl Application for Example { .spacing(5); let title_bar = pane_grid::TitleBar::new(title) - .controls(view_controls(id, total_panes, pane.is_pinned)) + .controls(view_controls( + id, + total_panes, + pane.is_pinned, + is_maximized, + )) .padding(10) .style(if is_focused { style::title_bar_focused @@ -314,16 +325,35 @@ fn view_controls<'a>( pane: pane_grid::Pane, total_panes: usize, is_pinned: bool, + is_maximized: bool, ) -> Element<'a, Message> { - let mut button = button(text("Close").size(14)) + let mut row = row![].spacing(5); + + if total_panes > 1 { + let toggle = { + let (content, message) = if is_maximized { + ("Restore", Message::Restore) + } else { + ("Maximize", Message::Maximize(pane)) + }; + button(text(content).size(14)) + .style(theme::Button::Secondary) + .padding(3) + .on_press(message) + }; + + row = row.push(toggle); + } + + let mut close = button(text("Close").size(14)) .style(theme::Button::Destructive) .padding(3); if total_panes > 1 && !is_pinned { - button = button.on_press(Message::Close(pane)); + close = close.on_press(Message::Close(pane)); } - button.into() + row.push(close).into() } mod style { From df7bf55ce162c5523f530a43113194a6c064a220 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Wed, 2 Nov 2022 17:12:38 -0700 Subject: [PATCH 3/9] Disable drag when maximized --- native/src/widget/pane_grid.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 1b55537f70..a58bfc77ae 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -221,6 +221,12 @@ where self.style = style.into(); self } + + fn drag_enabled(&self) -> bool { + (!self.elements.is_maximized()) + .then(|| self.on_drag.is_some()) + .unwrap_or_default() + } } impl<'a, Message, Renderer> Widget @@ -296,6 +302,11 @@ where ) -> event::Status { let action = tree.state.downcast_mut::(); + let on_drag = self + .drag_enabled() + .then_some(&self.on_drag) + .unwrap_or(&None); + let event_status = update( action, self.elements.node(self.state), @@ -306,7 +317,7 @@ where self.spacing, self.elements.iter(), &self.on_click, - &self.on_drag, + on_drag, &self.on_resize, ); @@ -361,7 +372,7 @@ where cursor_position, viewport, renderer, - self.on_drag.is_some(), + self.drag_enabled(), ) }) .max() @@ -961,4 +972,9 @@ impl Elements { Elements::Maximized(_, _, node) => node, } } + + /// TODO + pub fn is_maximized(&self) -> bool { + matches!(self, Self::Maximized(..)) + } } From 923878c7b7404739a3f8f2dd86a2e19802fa86de Mon Sep 17 00:00:00 2001 From: tarkah Date: Wed, 2 Nov 2022 19:00:07 -0700 Subject: [PATCH 4/9] Fix tests & lints --- native/src/widget/pane_grid.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index a58bfc77ae..94ef84a34a 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -85,7 +85,7 @@ use crate::{ /// let (mut state, _) = pane_grid::State::new(PaneState::SomePane); /// /// let pane_grid = -/// PaneGrid::new(&state, |pane, state| { +/// PaneGrid::new(&state, |pane, state, is_maximized| { /// pane_grid::Content::new(match state { /// PaneState::SomePane => text("This is some pane"), /// PaneState::AnotherKindOfPane => text("This is another kind of pane"), @@ -302,10 +302,11 @@ where ) -> event::Status { let action = tree.state.downcast_mut::(); - let on_drag = self - .drag_enabled() - .then_some(&self.on_drag) - .unwrap_or(&None); + let on_drag = if self.drag_enabled() { + &self.on_drag + } else { + &None + }; let event_status = update( action, From 988515d57f8c67a22ca0554f3e1327b26e5c6ecf Mon Sep 17 00:00:00 2001 From: tarkah Date: Wed, 2 Nov 2022 19:25:27 -0700 Subject: [PATCH 5/9] Add state::Scoped & rename Elements as Contents --- native/src/widget/pane_grid.rs | 176 +++++++++++++-------------- native/src/widget/pane_grid/state.rs | 18 ++- 2 files changed, 100 insertions(+), 94 deletions(-) diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 94ef84a34a..8864da0c85 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -100,8 +100,8 @@ where Renderer: crate::Renderer, Renderer::Theme: StyleSheet + container::StyleSheet, { - state: &'a state::Internal, - elements: Elements>, + state: state::Scoped<'a>, + contents: Contents>, width: Length, height: Length, spacing: u16, @@ -124,30 +124,32 @@ where state: &'a State, view: impl Fn(Pane, &'a T, bool) -> Content<'a, Message, Renderer>, ) -> Self { - let elements = if let Some((pane, pane_state)) = + let (contents, state) = if let Some((pane, pane_state)) = state.maximized.and_then(|pane| { state.panes.get(&pane).map(|pane_state| (pane, pane_state)) }) { - Elements::Maximized( - pane, - view(pane, pane_state, true), - Node::Pane(pane), + ( + Contents::Maximized(pane, view(pane, pane_state, true)), + state::Scoped::Maximized(Node::Pane(pane)), ) } else { - Elements::Normal( - state - .panes - .iter() - .map(|(pane, pane_state)| { - (*pane, view(*pane, pane_state, false)) - }) - .collect(), + ( + Contents::All( + state + .panes + .iter() + .map(|(pane, pane_state)| { + (*pane, view(*pane, pane_state, false)) + }) + .collect(), + ), + state::Scoped::All(&state.internal), ) }; Self { - elements, - state: &state.internal, + contents, + state, width: Length::Fill, height: Length::Fill, spacing: 0, @@ -223,7 +225,7 @@ where } fn drag_enabled(&self) -> bool { - (!self.elements.is_maximized()) + (!self.contents.is_maximized()) .then(|| self.on_drag.is_some()) .unwrap_or_default() } @@ -244,20 +246,20 @@ where } fn children(&self) -> Vec { - self.elements + self.contents .iter() .map(|(_, content)| content.state()) .collect() } fn diff(&self, tree: &mut Tree) { - match &self.elements { - Elements::Normal(elements) => tree.diff_children_custom( - elements, + match &self.contents { + Contents::All(contents) => tree.diff_children_custom( + contents, |state, (_, content)| content.diff(state), |(_, content)| content.state(), ), - Elements::Maximized(_, content, _) => tree.diff_children_custom( + Contents::Maximized(_, content) => tree.diff_children_custom( &[content], |state, content| content.diff(state), |content| content.state(), @@ -281,12 +283,12 @@ where layout( renderer, limits, - self.elements.node(self.state), + &self.state, self.width, self.height, self.spacing, - self.elements.iter(), - |element, renderer, limits| element.layout(renderer, limits), + self.contents.iter(), + |content, renderer, limits| content.layout(renderer, limits), ) } @@ -310,13 +312,13 @@ where let event_status = update( action, - self.elements.node(self.state), + &self.state, &event, layout, cursor_position, shell, self.spacing, - self.elements.iter(), + self.contents.iter(), &self.on_click, on_drag, &self.on_resize, @@ -324,7 +326,7 @@ where let picked_pane = action.picked_pane().map(|(pane, _)| pane); - self.elements + self.contents .iter_mut() .zip(&mut tree.children) .zip(layout.children()) @@ -355,14 +357,14 @@ where ) -> mouse::Interaction { mouse_interaction( tree.state.downcast_ref(), - self.elements.node(self.state), + &self.state, layout, cursor_position, self.spacing, self.on_resize.as_ref().map(|(leeway, _)| *leeway), ) .unwrap_or_else(|| { - self.elements + self.contents .iter() .zip(&tree.children) .zip(layout.children()) @@ -393,7 +395,7 @@ where ) { draw( tree.state.downcast_ref(), - self.elements.node(self.state), + &self.state, layout, cursor_position, renderer, @@ -403,7 +405,7 @@ where self.spacing, self.on_resize.as_ref().map(|(leeway, _)| *leeway), self.style, - self.elements + self.contents .iter() .zip(&tree.children) .map(|((pane, content), tree)| (pane, (content, tree))), @@ -432,7 +434,7 @@ where layout: Layout<'_>, renderer: &Renderer, ) -> Option> { - self.elements + self.contents .iter() .zip(&mut tree.children) .zip(layout.children()) @@ -458,27 +460,27 @@ where } /// Calculates the [`Layout`] of a [`PaneGrid`]. -pub fn layout( +pub fn layout<'a, Renderer, T>( renderer: &Renderer, limits: &layout::Limits, - node: &Node, + state: &state::Scoped<'a>, width: Length, height: Length, spacing: u16, - elements: impl Iterator, - layout_element: impl Fn(T, &Renderer, &layout::Limits) -> layout::Node, + contents: impl Iterator, + layout_content: impl Fn(T, &Renderer, &layout::Limits) -> layout::Node, ) -> layout::Node { let limits = limits.width(width).height(height); let size = limits.resolve(Size::ZERO); - let regions = node.pane_regions(f32::from(spacing), size); - let children = elements - .filter_map(|(pane, element)| { + let regions = state.layout().pane_regions(f32::from(spacing), size); + let children = contents + .filter_map(|(pane, content)| { let region = regions.get(&pane)?; let size = Size::new(region.width, region.height); - let mut node = layout_element( - element, + let mut node = layout_content( + content, renderer, &layout::Limits::new(size, size), ); @@ -496,13 +498,13 @@ pub fn layout( /// accordingly. pub fn update<'a, Message, T: Draggable>( action: &mut state::Action, - node: &Node, + state: &state::Scoped<'a>, event: &Event, layout: Layout<'_>, cursor_position: Point, shell: &mut Shell<'_, Message>, spacing: u16, - elements: impl Iterator, + contents: impl Iterator, on_click: &Option Message + 'a>>, on_drag: &Option Message + 'a>>, on_resize: &Option<(u16, Box Message + 'a>)>, @@ -524,7 +526,7 @@ pub fn update<'a, Message, T: Draggable>( cursor_position.y - bounds.y, ); - let splits = node.split_regions( + let splits = state.layout().split_regions( f32::from(spacing), Size::new(bounds.width, bounds.height), ); @@ -546,7 +548,7 @@ pub fn update<'a, Message, T: Draggable>( layout, cursor_position, shell, - elements, + contents, on_click, on_drag, ); @@ -558,7 +560,7 @@ pub fn update<'a, Message, T: Draggable>( layout, cursor_position, shell, - elements, + contents, on_click, on_drag, ); @@ -571,7 +573,7 @@ pub fn update<'a, Message, T: Draggable>( | Event::Touch(touch::Event::FingerLost { .. }) => { if let Some((pane, _)) = action.picked_pane() { if let Some(on_drag) = on_drag { - let mut dropped_region = elements + let mut dropped_region = contents .zip(layout.children()) .filter(|(_, layout)| { layout.bounds().contains(cursor_position) @@ -602,7 +604,7 @@ pub fn update<'a, Message, T: Draggable>( if let Some((split, _)) = action.picked_split() { let bounds = layout.bounds(); - let splits = node.split_regions( + let splits = state.layout().split_regions( f32::from(spacing), Size::new(bounds.width, bounds.height), ); @@ -641,13 +643,13 @@ fn click_pane<'a, Message, T>( layout: Layout<'_>, cursor_position: Point, shell: &mut Shell<'_, Message>, - elements: impl Iterator, + contents: impl Iterator, on_click: &Option Message + 'a>>, on_drag: &Option Message + 'a>>, ) where T: Draggable, { - let mut clicked_region = elements + let mut clicked_region = contents .zip(layout.children()) .filter(|(_, layout)| layout.bounds().contains(cursor_position)); @@ -672,9 +674,9 @@ fn click_pane<'a, Message, T>( } /// Returns the current [`mouse::Interaction`] of a [`PaneGrid`]. -pub fn mouse_interaction( +pub fn mouse_interaction<'a>( action: &state::Action, - node: &Node, + state: &state::Scoped<'a>, layout: Layout<'_>, cursor_position: Point, spacing: u16, @@ -689,8 +691,9 @@ pub fn mouse_interaction( resize_leeway.and_then(|leeway| { let bounds = layout.bounds(); - let splits = - node.split_regions(f32::from(spacing), bounds.size()); + let splits = state + .layout() + .split_regions(f32::from(spacing), bounds.size()); let relative_cursor = Point::new( cursor_position.x - bounds.x, @@ -717,9 +720,9 @@ pub fn mouse_interaction( } /// Draws a [`PaneGrid`]. -pub fn draw( +pub fn draw<'a, Renderer, T>( action: &state::Action, - node: &Node, + state: &state::Scoped<'a>, layout: Layout<'_>, cursor_position: Point, renderer: &mut Renderer, @@ -729,7 +732,7 @@ pub fn draw( spacing: u16, resize_leeway: Option, style: ::Style, - elements: impl Iterator, + contents: impl Iterator, draw_pane: impl Fn( T, &mut Renderer, @@ -749,7 +752,9 @@ pub fn draw( .and_then(|(split, axis)| { let bounds = layout.bounds(); - let splits = node.split_regions(f32::from(spacing), bounds.size()); + let splits = state + .layout() + .split_regions(f32::from(spacing), bounds.size()); let (_axis, region, ratio) = splits.get(&split)?; @@ -767,8 +772,9 @@ pub fn draw( cursor_position.y - bounds.y, ); - let splits = - node.split_regions(f32::from(spacing), bounds.size()); + let splits = state + .layout() + .split_regions(f32::from(spacing), bounds.size()); let (_split, axis, region) = hovered_split( splits.iter(), @@ -791,7 +797,7 @@ pub fn draw( let mut render_picked_pane = None; - for ((id, pane), layout) in elements.zip(layout.children()) { + for ((id, pane), layout) in contents.zip(layout.children()) { match picked_pane { Some((dragging, origin)) if id == dragging => { render_picked_pane = Some((pane, origin, layout)); @@ -930,52 +936,40 @@ fn hovered_split<'a>( .next() } -/// TODO +/// The visible contents of the [`PaneGrid`] #[derive(Debug)] -pub enum Elements { - /// TODO - Normal(Vec<(Pane, T)>), - /// TODO - Maximized(Pane, T, Node), +pub enum Contents { + /// All panes are visible + All(Vec<(Pane, T)>), + /// A maximized pane is visible + Maximized(Pane, T), } -impl Elements { - /// TODO +impl Contents { + /// Returns an iterator over the values of the [`Contents`] pub fn iter(&self) -> Box + '_> { match self { - Elements::Normal(elements) => Box::new( - elements.iter().map(|(pane, content)| (*pane, content)), + Contents::All(contents) => Box::new( + contents.iter().map(|(pane, content)| (*pane, content)), ), - Elements::Maximized(pane, content, _) => { + Contents::Maximized(pane, content) => { Box::new(std::iter::once((*pane, content))) } } } - /// TODO - pub fn iter_mut( - &mut self, - ) -> Box + '_> { + fn iter_mut(&mut self) -> Box + '_> { match self { - Elements::Normal(elements) => Box::new( - elements.iter_mut().map(|(pane, content)| (*pane, content)), + Contents::All(contents) => Box::new( + contents.iter_mut().map(|(pane, content)| (*pane, content)), ), - Elements::Maximized(pane, content, _) => { + Contents::Maximized(pane, content) => { Box::new(std::iter::once((*pane, content))) } } } - /// TODO - pub fn node<'a>(&'a self, state: &'a state::Internal) -> &'a Node { - match self { - Elements::Normal(_) => state.layout(), - Elements::Maximized(_, _, node) => node, - } - } - - /// TODO - pub fn is_maximized(&self) -> bool { + fn is_maximized(&self) -> bool { matches!(self, Self::Maximized(..)) } } diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs index 92d26f5a16..b5bebc2ed8 100644 --- a/native/src/widget/pane_grid/state.rs +++ b/native/src/widget/pane_grid/state.rs @@ -281,6 +281,15 @@ impl Internal { } } +/// The scoped internal state of the [`PaneGrid`] +#[derive(Debug)] +pub enum Scoped<'a> { + /// The state when all panes are visible + All(&'a Internal), + /// The state when a pane is maximized + Maximized(Node), +} + /// The current action of a [`PaneGrid`]. /// /// [`PaneGrid`]: crate::widget::PaneGrid @@ -328,9 +337,12 @@ impl Action { } } -impl Internal { - /// The layout [`Node`] of the [`Internal`] state +impl<'a> Scoped<'a> { + /// The layout [`Node`] of the [`Scope`] state pub fn layout(&self) -> &Node { - &self.layout + match self { + Scoped::All(Internal { layout, .. }) => layout, + Scoped::Maximized(layout) => layout, + } } } From 2f6c71d99a2c739c8b86bdf9d024e83ae994042d Mon Sep 17 00:00:00 2001 From: tarkah Date: Wed, 2 Nov 2022 19:54:49 -0700 Subject: [PATCH 6/9] Fix doc links --- native/src/widget/pane_grid/state.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs index b5bebc2ed8..70a2aa885e 100644 --- a/native/src/widget/pane_grid/state.rs +++ b/native/src/widget/pane_grid/state.rs @@ -214,12 +214,16 @@ impl State { /// Maximize the given [`Pane`]. Only this pane will be rendered by the /// [`PaneGrid`] until [`Self::restore()`] is called. + /// + /// [`PaneGrid`]: crate::widget::PaneGrid pub fn maximize(&mut self, pane: &Pane) { self.maximized = Some(*pane); } /// Restore the currently maximized [`Pane`] to it's normal size. All panes - /// will be rendered by the [`PaneGrid`] + /// will be rendered by the [`PaneGrid`]. + /// + /// [`PaneGrid`]: crate::widget::PaneGrid pub fn restore(&mut self) { let _ = self.maximized.take(); } @@ -282,6 +286,8 @@ impl Internal { } /// The scoped internal state of the [`PaneGrid`] +/// +/// [`PaneGrid`]: crate::widget::PaneGrid #[derive(Debug)] pub enum Scoped<'a> { /// The state when all panes are visible @@ -338,7 +344,7 @@ impl Action { } impl<'a> Scoped<'a> { - /// The layout [`Node`] of the [`Scope`] state + /// The layout [`Node`] of the [`Scoped`] state pub fn layout(&self) -> &Node { match self { Scoped::All(Internal { layout, .. }) => layout, From 951fbc83ff8878be03eb6c8c43f2a28d0f0f0d4c Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Thu, 3 Nov 2022 08:02:20 -0700 Subject: [PATCH 7/9] Remove maximized when split occurs --- native/src/widget/pane_grid/state.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs index 70a2aa885e..882a45f232 100644 --- a/native/src/widget/pane_grid/state.rs +++ b/native/src/widget/pane_grid/state.rs @@ -160,6 +160,7 @@ impl State { node.split(new_split, axis, new_pane); let _ = self.panes.insert(new_pane, state); + let _ = self.maximized.take(); Some((new_pane, new_split)) } From 853ff4bcf4eff24808c680f9a35bfc0013cb779d Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Thu, 3 Nov 2022 11:32:36 -0700 Subject: [PATCH 8/9] Add pub method for getting maximized value --- native/src/widget/pane_grid/state.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs index 882a45f232..c9e9433d09 100644 --- a/native/src/widget/pane_grid/state.rs +++ b/native/src/widget/pane_grid/state.rs @@ -228,6 +228,13 @@ impl State { pub fn restore(&mut self) { let _ = self.maximized.take(); } + + /// Returns the maximized [`Pane`] of the [`PaneGrid`]. + /// + /// [`PaneGrid`]: crate::widget::PaneGrid + pub fn maximized(&self) -> Option { + self.maximized + } } /// The internal state of a [`PaneGrid`]. From 7de9d2475dbf4ed93c4248580514901f82a0fc0e Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Tue, 8 Nov 2022 08:49:26 -0800 Subject: [PATCH 9/9] Couple layout & content to avoid desync --- native/src/widget/pane_grid.rs | 103 ++++++++++++++------------- native/src/widget/pane_grid/state.rs | 20 +----- 2 files changed, 55 insertions(+), 68 deletions(-) diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 8864da0c85..fd771f8b59 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -100,8 +100,7 @@ where Renderer: crate::Renderer, Renderer::Theme: StyleSheet + container::StyleSheet, { - state: state::Scoped<'a>, - contents: Contents>, + contents: Contents<'a, Content<'a, Message, Renderer>>, width: Length, height: Length, spacing: u16, @@ -124,32 +123,30 @@ where state: &'a State, view: impl Fn(Pane, &'a T, bool) -> Content<'a, Message, Renderer>, ) -> Self { - let (contents, state) = if let Some((pane, pane_state)) = + 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)), - state::Scoped::Maximized(Node::Pane(pane)), + 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::Scoped::All(&state.internal), + Contents::All( + state + .panes + .iter() + .map(|(pane, pane_state)| { + (*pane, view(*pane, pane_state, false)) + }) + .collect(), + &state.internal, ) }; Self { contents, - state, width: Length::Fill, height: Length::Fill, spacing: 0, @@ -254,12 +251,12 @@ where fn diff(&self, tree: &mut Tree) { match &self.contents { - Contents::All(contents) => tree.diff_children_custom( + Contents::All(contents, _) => tree.diff_children_custom( contents, |state, (_, content)| content.diff(state), |(_, content)| content.state(), ), - Contents::Maximized(_, content) => tree.diff_children_custom( + Contents::Maximized(_, content, _) => tree.diff_children_custom( &[content], |state, content| content.diff(state), |content| content.state(), @@ -283,7 +280,7 @@ where layout( renderer, limits, - &self.state, + self.contents.layout(), self.width, self.height, self.spacing, @@ -312,7 +309,7 @@ where let event_status = update( action, - &self.state, + self.contents.layout(), &event, layout, cursor_position, @@ -357,7 +354,7 @@ where ) -> mouse::Interaction { mouse_interaction( tree.state.downcast_ref(), - &self.state, + self.contents.layout(), layout, cursor_position, self.spacing, @@ -395,7 +392,7 @@ where ) { draw( tree.state.downcast_ref(), - &self.state, + self.contents.layout(), layout, cursor_position, renderer, @@ -460,10 +457,10 @@ where } /// Calculates the [`Layout`] of a [`PaneGrid`]. -pub fn layout<'a, Renderer, T>( +pub fn layout( renderer: &Renderer, limits: &layout::Limits, - state: &state::Scoped<'a>, + node: &Node, width: Length, height: Length, spacing: u16, @@ -473,7 +470,7 @@ pub fn layout<'a, Renderer, T>( let limits = limits.width(width).height(height); let size = limits.resolve(Size::ZERO); - let regions = state.layout().pane_regions(f32::from(spacing), size); + let regions = node.pane_regions(f32::from(spacing), size); let children = contents .filter_map(|(pane, content)| { let region = regions.get(&pane)?; @@ -498,7 +495,7 @@ pub fn layout<'a, Renderer, T>( /// accordingly. pub fn update<'a, Message, T: Draggable>( action: &mut state::Action, - state: &state::Scoped<'a>, + node: &Node, event: &Event, layout: Layout<'_>, cursor_position: Point, @@ -526,7 +523,7 @@ pub fn update<'a, Message, T: Draggable>( cursor_position.y - bounds.y, ); - let splits = state.layout().split_regions( + let splits = node.split_regions( f32::from(spacing), Size::new(bounds.width, bounds.height), ); @@ -604,7 +601,7 @@ pub fn update<'a, Message, T: Draggable>( if let Some((split, _)) = action.picked_split() { let bounds = layout.bounds(); - let splits = state.layout().split_regions( + let splits = node.split_regions( f32::from(spacing), Size::new(bounds.width, bounds.height), ); @@ -674,9 +671,9 @@ fn click_pane<'a, Message, T>( } /// Returns the current [`mouse::Interaction`] of a [`PaneGrid`]. -pub fn mouse_interaction<'a>( +pub fn mouse_interaction( action: &state::Action, - state: &state::Scoped<'a>, + node: &Node, layout: Layout<'_>, cursor_position: Point, spacing: u16, @@ -691,9 +688,8 @@ pub fn mouse_interaction<'a>( resize_leeway.and_then(|leeway| { let bounds = layout.bounds(); - let splits = state - .layout() - .split_regions(f32::from(spacing), bounds.size()); + let splits = + node.split_regions(f32::from(spacing), bounds.size()); let relative_cursor = Point::new( cursor_position.x - bounds.x, @@ -720,9 +716,9 @@ pub fn mouse_interaction<'a>( } /// Draws a [`PaneGrid`]. -pub fn draw<'a, Renderer, T>( +pub fn draw( action: &state::Action, - state: &state::Scoped<'a>, + node: &Node, layout: Layout<'_>, cursor_position: Point, renderer: &mut Renderer, @@ -752,9 +748,7 @@ pub fn draw<'a, Renderer, T>( .and_then(|(split, axis)| { let bounds = layout.bounds(); - let splits = state - .layout() - .split_regions(f32::from(spacing), bounds.size()); + let splits = node.split_regions(f32::from(spacing), bounds.size()); let (_axis, region, ratio) = splits.get(&split)?; @@ -772,9 +766,8 @@ pub fn draw<'a, Renderer, T>( cursor_position.y - bounds.y, ); - let splits = state - .layout() - .split_regions(f32::from(spacing), bounds.size()); + let splits = + node.split_regions(f32::from(spacing), bounds.size()); let (_split, axis, region) = hovered_split( splits.iter(), @@ -938,21 +931,29 @@ fn hovered_split<'a>( /// The visible contents of the [`PaneGrid`] #[derive(Debug)] -pub enum Contents { +pub enum Contents<'a, T> { /// All panes are visible - All(Vec<(Pane, T)>), + All(Vec<(Pane, T)>, &'a state::Internal), /// A maximized pane is visible - Maximized(Pane, T), + Maximized(Pane, T, Node), } -impl Contents { +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::All(contents, _) => Box::new( contents.iter().map(|(pane, content)| (*pane, content)), ), - Contents::Maximized(pane, content) => { + Contents::Maximized(pane, content, _) => { Box::new(std::iter::once((*pane, content))) } } @@ -960,10 +961,10 @@ impl Contents { fn iter_mut(&mut self) -> Box + '_> { match self { - Contents::All(contents) => Box::new( + Contents::All(contents, _) => Box::new( contents.iter_mut().map(|(pane, content)| (*pane, content)), ), - Contents::Maximized(pane, content) => { + Contents::Maximized(pane, content, _) => { Box::new(std::iter::once((*pane, content))) } } diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs index c9e9433d09..58397444c4 100644 --- a/native/src/widget/pane_grid/state.rs +++ b/native/src/widget/pane_grid/state.rs @@ -293,17 +293,6 @@ impl Internal { } } -/// The scoped internal state of the [`PaneGrid`] -/// -/// [`PaneGrid`]: crate::widget::PaneGrid -#[derive(Debug)] -pub enum Scoped<'a> { - /// The state when all panes are visible - All(&'a Internal), - /// The state when a pane is maximized - Maximized(Node), -} - /// The current action of a [`PaneGrid`]. /// /// [`PaneGrid`]: crate::widget::PaneGrid @@ -351,12 +340,9 @@ impl Action { } } -impl<'a> Scoped<'a> { - /// The layout [`Node`] of the [`Scoped`] state +impl Internal { + /// The layout [`Node`] of the [`Internal`] state pub fn layout(&self) -> &Node { - match self { - Scoped::All(Internal { layout, .. }) => layout, - Scoped::Maximized(layout) => layout, - } + &self.layout } }