Add maximize / restore to PaneGrid

This commit is contained in:
Cory Forsstrom 2022-11-02 16:49:18 -07:00
parent 1c00adad61
commit b761ab5e1d
2 changed files with 128 additions and 55 deletions

View file

@ -101,7 +101,7 @@ where
Renderer::Theme: StyleSheet + container::StyleSheet, Renderer::Theme: StyleSheet + container::StyleSheet,
{ {
state: &'a state::Internal, state: &'a state::Internal,
elements: Vec<(Pane, Content<'a, Message, Renderer>)>, elements: Elements<Content<'a, Message, Renderer>>,
width: Length, width: Length,
height: Length, height: Length,
spacing: u16, spacing: u16,
@ -119,17 +119,30 @@ where
/// Creates a [`PaneGrid`] with the given [`State`] and view function. /// Creates a [`PaneGrid`] with the given [`State`] and view function.
/// ///
/// The view function will be called to display each [`Pane`] present in the /// 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>( pub fn new<T>(
state: &'a State<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 { ) -> Self {
let elements = { let elements = if let Some((pane, pane_state)) =
state state.maximized.and_then(|pane| {
.panes state.panes.get(&pane).map(|pane_state| (pane, pane_state))
.iter() }) {
.map(|(pane, pane_state)| (*pane, view(*pane, pane_state))) Elements::Maximized(
.collect() 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 { Self {
@ -232,11 +245,18 @@ where
} }
fn diff(&self, tree: &mut Tree) { fn diff(&self, tree: &mut Tree) {
tree.diff_children_custom( match &self.elements {
&self.elements, Elements::Normal(elements) => tree.diff_children_custom(
|state, (_, content)| content.diff(state), elements,
|(_, content)| content.state(), |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 { fn width(&self) -> Length {
@ -255,11 +275,11 @@ where
layout( layout(
renderer, renderer,
limits, limits,
self.state, self.elements.node(self.state),
self.width, self.width,
self.height, self.height,
self.spacing, self.spacing,
self.elements.iter().map(|(pane, content)| (*pane, content)), self.elements.iter(),
|element, renderer, limits| element.layout(renderer, limits), |element, renderer, limits| element.layout(renderer, limits),
) )
} }
@ -278,13 +298,13 @@ where
let event_status = update( let event_status = update(
action, action,
self.state, self.elements.node(self.state),
&event, &event,
layout, layout,
cursor_position, cursor_position,
shell, shell,
self.spacing, self.spacing,
self.elements.iter().map(|(pane, content)| (*pane, content)), self.elements.iter(),
&self.on_click, &self.on_click,
&self.on_drag, &self.on_drag,
&self.on_resize, &self.on_resize,
@ -297,7 +317,7 @@ where
.zip(&mut tree.children) .zip(&mut tree.children)
.zip(layout.children()) .zip(layout.children())
.map(|(((pane, content), tree), layout)| { .map(|(((pane, content), tree), layout)| {
let is_picked = picked_pane == Some(*pane); let is_picked = picked_pane == Some(pane);
content.on_event( content.on_event(
tree, tree,
@ -323,7 +343,7 @@ where
) -> mouse::Interaction { ) -> mouse::Interaction {
mouse_interaction( mouse_interaction(
tree.state.downcast_ref(), tree.state.downcast_ref(),
self.state, self.elements.node(self.state),
layout, layout,
cursor_position, cursor_position,
self.spacing, self.spacing,
@ -361,7 +381,7 @@ where
) { ) {
draw( draw(
tree.state.downcast_ref(), tree.state.downcast_ref(),
self.state, self.elements.node(self.state),
layout, layout,
cursor_position, cursor_position,
renderer, renderer,
@ -374,7 +394,7 @@ where
self.elements self.elements
.iter() .iter()
.zip(&tree.children) .zip(&tree.children)
.map(|((pane, content), tree)| (*pane, (content, tree))), .map(|((pane, content), tree)| (pane, (content, tree))),
|(content, tree), |(content, tree),
renderer, renderer,
style, style,
@ -429,7 +449,7 @@ where
pub fn layout<Renderer, T>( pub fn layout<Renderer, T>(
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
state: &state::Internal, node: &Node,
width: Length, width: Length,
height: Length, height: Length,
spacing: u16, spacing: u16,
@ -439,7 +459,7 @@ pub fn layout<Renderer, T>(
let limits = limits.width(width).height(height); let limits = limits.width(width).height(height);
let size = limits.resolve(Size::ZERO); 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 let children = elements
.filter_map(|(pane, element)| { .filter_map(|(pane, element)| {
let region = regions.get(&pane)?; let region = regions.get(&pane)?;
@ -464,7 +484,7 @@ pub fn layout<Renderer, T>(
/// accordingly. /// accordingly.
pub fn update<'a, Message, T: Draggable>( pub fn update<'a, Message, T: Draggable>(
action: &mut state::Action, action: &mut state::Action,
state: &state::Internal, node: &Node,
event: &Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
@ -492,7 +512,7 @@ pub fn update<'a, Message, T: Draggable>(
cursor_position.y - bounds.y, cursor_position.y - bounds.y,
); );
let splits = state.split_regions( let splits = node.split_regions(
f32::from(spacing), f32::from(spacing),
Size::new(bounds.width, bounds.height), Size::new(bounds.width, bounds.height),
); );
@ -570,7 +590,7 @@ pub fn update<'a, Message, T: Draggable>(
if let Some((split, _)) = action.picked_split() { if let Some((split, _)) = action.picked_split() {
let bounds = layout.bounds(); let bounds = layout.bounds();
let splits = state.split_regions( let splits = node.split_regions(
f32::from(spacing), f32::from(spacing),
Size::new(bounds.width, bounds.height), Size::new(bounds.width, bounds.height),
); );
@ -642,7 +662,7 @@ fn click_pane<'a, Message, T>(
/// Returns the current [`mouse::Interaction`] of a [`PaneGrid`]. /// Returns the current [`mouse::Interaction`] of a [`PaneGrid`].
pub fn mouse_interaction( pub fn mouse_interaction(
action: &state::Action, action: &state::Action,
state: &state::Internal, node: &Node,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
spacing: u16, spacing: u16,
@ -658,7 +678,7 @@ pub fn mouse_interaction(
let bounds = layout.bounds(); let bounds = layout.bounds();
let splits = let splits =
state.split_regions(f32::from(spacing), bounds.size()); node.split_regions(f32::from(spacing), bounds.size());
let relative_cursor = Point::new( let relative_cursor = Point::new(
cursor_position.x - bounds.x, cursor_position.x - bounds.x,
@ -687,7 +707,7 @@ pub fn mouse_interaction(
/// Draws a [`PaneGrid`]. /// Draws a [`PaneGrid`].
pub fn draw<Renderer, T>( pub fn draw<Renderer, T>(
action: &state::Action, action: &state::Action,
state: &state::Internal, node: &Node,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
renderer: &mut Renderer, renderer: &mut Renderer,
@ -717,7 +737,7 @@ pub fn draw<Renderer, T>(
.and_then(|(split, axis)| { .and_then(|(split, axis)| {
let bounds = layout.bounds(); 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)?; let (_axis, region, ratio) = splits.get(&split)?;
@ -736,7 +756,7 @@ pub fn draw<Renderer, T>(
); );
let splits = let splits =
state.split_regions(f32::from(spacing), bounds.size()); node.split_regions(f32::from(spacing), bounds.size());
let (_split, axis, region) = hovered_split( let (_split, axis, region) = hovered_split(
splits.iter(), splits.iter(),
@ -897,3 +917,48 @@ fn hovered_split<'a>(
}) })
.next() .next()
} }
/// TODO
#[derive(Debug)]
pub enum Elements<T> {
/// TODO
Normal(Vec<(Pane, T)>),
/// TODO
Maximized(Pane, T, Node),
}
impl<T> Elements<T> {
/// TODO
pub fn iter(&self) -> Box<dyn Iterator<Item = (Pane, &T)> + '_> {
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<dyn Iterator<Item = (Pane, &mut T)> + '_> {
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,
}
}
}

View file

@ -4,9 +4,9 @@
use crate::widget::pane_grid::{ use crate::widget::pane_grid::{
Axis, Configuration, Direction, Node, Pane, Split, 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`]. /// The state of a [`PaneGrid`].
/// ///
@ -31,6 +31,9 @@ pub struct State<T> {
/// ///
/// [`PaneGrid`]: crate::widget::PaneGrid /// [`PaneGrid`]: crate::widget::PaneGrid
pub internal: Internal, pub internal: Internal,
/// The maximized [`Pane`] of the [`PaneGrid`]
pub(super) maximized: Option<Pane>,
} }
impl<T> State<T> { impl<T> State<T> {
@ -52,7 +55,11 @@ impl<T> State<T> {
let internal = let internal =
Internal::from_configuration(&mut panes, config.into(), 0); 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`]. /// Returns the total amount of panes in the [`State`].
@ -194,12 +201,28 @@ impl<T> State<T> {
/// Closes the given [`Pane`] and returns its internal state and its closest /// Closes the given [`Pane`] and returns its internal state and its closest
/// sibling, if it exists. /// sibling, if it exists.
pub fn close(&mut self, pane: &Pane) -> Option<(T, Pane)> { 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) { if let Some(sibling) = self.internal.layout.remove(pane) {
self.panes.remove(pane).map(|state| (state, sibling)) self.panes.remove(pane).map(|state| (state, sibling))
} else { } else {
None 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`]. /// The internal state of a [`PaneGrid`].
@ -226,11 +249,13 @@ impl Internal {
let Internal { let Internal {
layout: a, layout: a,
last_id: next_id, last_id: next_id,
..
} = Self::from_configuration(panes, *a, next_id); } = Self::from_configuration(panes, *a, next_id);
let Internal { let Internal {
layout: b, layout: b,
last_id: next_id, last_id: next_id,
..
} = Self::from_configuration(panes, *b, next_id); } = Self::from_configuration(panes, *b, next_id);
( (
@ -304,25 +329,8 @@ impl Action {
} }
impl Internal { impl Internal {
/// Calculates the current [`Pane`] regions from the [`PaneGrid`] layout. /// The layout [`Node`] of the [`Internal`] state
/// pub fn layout(&self) -> &Node {
/// [`PaneGrid`]: crate::widget::PaneGrid &self.layout
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)
} }
} }