diff options
Diffstat (limited to 'native/src/widget')
-rw-r--r-- | native/src/widget/pane_grid.rs | 193 | ||||
-rw-r--r-- | native/src/widget/pane_grid/state.rs | 66 |
2 files changed, 178 insertions, 81 deletions
diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 96cf78ef..fd771f8b 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"), @@ -100,8 +100,7 @@ where Renderer: crate::Renderer, Renderer::Theme: StyleSheet + container::StyleSheet, { - state: &'a state::Internal, - elements: Vec<(Pane, Content<'a, Message, Renderer>)>, + contents: Contents<'a, Content<'a, Message, Renderer>>, width: Length, height: Length, spacing: u16, @@ -119,22 +118,35 @@ 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<T>( state: &'a State<T>, - 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 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, + ) }; Self { - elements, - state: &state.internal, + contents, width: Length::Fill, height: Length::Fill, spacing: 0, @@ -208,6 +220,12 @@ where self.style = style.into(); self } + + fn drag_enabled(&self) -> bool { + (!self.contents.is_maximized()) + .then(|| self.on_drag.is_some()) + .unwrap_or_default() + } } impl<'a, Message, Renderer> Widget<Message, Renderer> @@ -225,18 +243,25 @@ where } fn children(&self) -> Vec<Tree> { - self.elements + self.contents .iter() .map(|(_, content)| content.state()) .collect() } fn diff(&self, tree: &mut Tree) { - tree.diff_children_custom( - &self.elements, - |state, (_, content)| content.diff(state), - |(_, content)| content.state(), - ) + 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(), + ), + } } fn width(&self) -> Length { @@ -255,12 +280,12 @@ where layout( renderer, limits, - self.state, + self.contents.layout(), self.width, self.height, self.spacing, - self.elements.iter().map(|(pane, content)| (*pane, content)), - |element, renderer, limits| element.layout(renderer, limits), + self.contents.iter(), + |content, renderer, limits| content.layout(renderer, limits), ) } @@ -276,28 +301,34 @@ where ) -> event::Status { let action = tree.state.downcast_mut::<state::Action>(); + let on_drag = if self.drag_enabled() { + &self.on_drag + } else { + &None + }; + let event_status = update( action, - self.state, + self.contents.layout(), &event, layout, cursor_position, shell, self.spacing, - self.elements.iter().map(|(pane, content)| (*pane, content)), + self.contents.iter(), &self.on_click, - &self.on_drag, + on_drag, &self.on_resize, ); let picked_pane = action.picked_pane().map(|(pane, _)| pane); - self.elements + self.contents .iter_mut() .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,14 +354,14 @@ where ) -> mouse::Interaction { mouse_interaction( tree.state.downcast_ref(), - self.state, + self.contents.layout(), 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()) @@ -341,7 +372,7 @@ where cursor_position, viewport, renderer, - self.on_drag.is_some(), + self.drag_enabled(), ) }) .max() @@ -361,7 +392,7 @@ where ) { draw( tree.state.downcast_ref(), - self.state, + self.contents.layout(), layout, cursor_position, renderer, @@ -371,10 +402,10 @@ 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))), + .map(|((pane, content), tree)| (pane, (content, tree))), |(content, tree), renderer, style, @@ -400,7 +431,7 @@ where layout: Layout<'_>, renderer: &Renderer, ) -> Option<overlay::Element<'_, Message, Renderer>> { - self.elements + self.contents .iter() .zip(&mut tree.children) .zip(layout.children()) @@ -429,24 +460,24 @@ where pub fn layout<Renderer, T>( renderer: &Renderer, limits: &layout::Limits, - state: &state::Internal, + node: &Node, width: Length, height: Length, spacing: u16, - elements: impl Iterator<Item = (Pane, T)>, - layout_element: impl Fn(T, &Renderer, &layout::Limits) -> layout::Node, + contents: impl Iterator<Item = (Pane, T)>, + 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 = state.pane_regions(f32::from(spacing), size); - let children = elements - .filter_map(|(pane, element)| { + let regions = node.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), ); @@ -464,13 +495,13 @@ pub fn layout<Renderer, T>( /// accordingly. pub fn update<'a, Message, T: Draggable>( action: &mut state::Action, - state: &state::Internal, + node: &Node, event: &Event, layout: Layout<'_>, cursor_position: Point, shell: &mut Shell<'_, Message>, spacing: u16, - elements: impl Iterator<Item = (Pane, T)>, + contents: impl Iterator<Item = (Pane, T)>, on_click: &Option<Box<dyn Fn(Pane) -> Message + 'a>>, on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>, on_resize: &Option<(u16, Box<dyn Fn(ResizeEvent) -> Message + 'a>)>, @@ -492,7 +523,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), ); @@ -514,7 +545,7 @@ pub fn update<'a, Message, T: Draggable>( layout, cursor_position, shell, - elements, + contents, on_click, on_drag, ); @@ -526,7 +557,7 @@ pub fn update<'a, Message, T: Draggable>( layout, cursor_position, shell, - elements, + contents, on_click, on_drag, ); @@ -539,7 +570,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) @@ -570,7 +601,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), ); @@ -609,13 +640,13 @@ fn click_pane<'a, Message, T>( layout: Layout<'_>, cursor_position: Point, shell: &mut Shell<'_, Message>, - elements: impl Iterator<Item = (Pane, T)>, + contents: impl Iterator<Item = (Pane, T)>, on_click: &Option<Box<dyn Fn(Pane) -> Message + 'a>>, on_drag: &Option<Box<dyn Fn(DragEvent) -> 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)); @@ -642,7 +673,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 +689,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 +718,7 @@ pub fn mouse_interaction( /// Draws a [`PaneGrid`]. pub fn draw<Renderer, T>( action: &state::Action, - state: &state::Internal, + node: &Node, layout: Layout<'_>, cursor_position: Point, renderer: &mut Renderer, @@ -697,7 +728,7 @@ pub fn draw<Renderer, T>( spacing: u16, resize_leeway: Option<u16>, style: <Renderer::Theme as StyleSheet>::Style, - elements: impl Iterator<Item = (Pane, T)>, + contents: impl Iterator<Item = (Pane, T)>, draw_pane: impl Fn( T, &mut Renderer, @@ -717,7 +748,7 @@ pub fn draw<Renderer, T>( .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 +767,7 @@ pub fn draw<Renderer, T>( ); 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(), @@ -759,7 +790,7 @@ pub fn draw<Renderer, T>( 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)); @@ -897,3 +928,49 @@ fn hovered_split<'a>( }) .next() } + +/// 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<dyn Iterator<Item = (Pane, &T)> + '_> { + 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<dyn Iterator<Item = (Pane, &mut T)> + '_> { + 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(..)) + } +} diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs index cdca6267..58397444 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<T> { /// /// [`PaneGrid`]: crate::widget::PaneGrid pub internal: Internal, + + /// The maximized [`Pane`] of the [`PaneGrid`] + pub(super) maximized: Option<Pane>, } impl<T> State<T> { @@ -52,7 +55,11 @@ impl<T> State<T> { 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`]. @@ -153,6 +160,7 @@ impl<T> State<T> { node.split(new_split, axis, new_pane); let _ = self.panes.insert(new_pane, state); + let _ = self.maximized.take(); Some((new_pane, new_split)) } @@ -194,12 +202,39 @@ impl<T> State<T> { /// 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. + /// + /// [`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`]. + /// + /// [`PaneGrid`]: crate::widget::PaneGrid + 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<Pane> { + self.maximized + } } /// The internal state of a [`PaneGrid`]. @@ -226,11 +261,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 +341,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<Pane, Rectangle> { - 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<Split, (Axis, Rectangle, f32)> { - self.layout.split_regions(spacing, size) + /// The layout [`Node`] of the [`Internal`] state + pub fn layout(&self) -> &Node { + &self.layout } } |