diff options
| author | 2022-11-08 18:12:06 +0100 | |
|---|---|---|
| committer | 2022-11-08 18:12:06 +0100 | |
| commit | d9f408d1c2567698c97890b1bbcc4363bcd313aa (patch) | |
| tree | cecd0510a60de87e59977284ddf9fc0140aedea1 /native/src | |
| parent | ac737cb6e905f7a8a85e9f26a27deb7907e80114 (diff) | |
| parent | 7de9d2475dbf4ed93c4248580514901f82a0fc0e (diff) | |
| download | iced-d9f408d1c2567698c97890b1bbcc4363bcd313aa.tar.gz iced-d9f408d1c2567698c97890b1bbcc4363bcd313aa.tar.bz2 iced-d9f408d1c2567698c97890b1bbcc4363bcd313aa.zip  | |
Merge pull request #1504 from tarkah/feat/pane-grid-maximize
Add pane maximize / restore for `PaneGrid`
Diffstat (limited to '')
| -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      }  }  | 
