Merge pull request #1504 from tarkah/feat/pane-grid-maximize
Add pane maximize / restore for `PaneGrid`
This commit is contained in:
commit
d9f408d1c2
3 changed files with 213 additions and 86 deletions
|
|
@ -29,6 +29,8 @@ enum Message {
|
||||||
Dragged(pane_grid::DragEvent),
|
Dragged(pane_grid::DragEvent),
|
||||||
Resized(pane_grid::ResizeEvent),
|
Resized(pane_grid::ResizeEvent),
|
||||||
TogglePin(pane_grid::Pane),
|
TogglePin(pane_grid::Pane),
|
||||||
|
Maximize(pane_grid::Pane),
|
||||||
|
Restore,
|
||||||
Close(pane_grid::Pane),
|
Close(pane_grid::Pane),
|
||||||
CloseFocused,
|
CloseFocused,
|
||||||
}
|
}
|
||||||
|
|
@ -114,6 +116,10 @@ impl Application for Example {
|
||||||
*is_pinned = !*is_pinned;
|
*is_pinned = !*is_pinned;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Message::Maximize(pane) => self.panes.maximize(&pane),
|
||||||
|
Message::Restore => {
|
||||||
|
self.panes.restore();
|
||||||
|
}
|
||||||
Message::Close(pane) => {
|
Message::Close(pane) => {
|
||||||
if let Some((_, sibling)) = self.panes.close(&pane) {
|
if let Some((_, sibling)) = self.panes.close(&pane) {
|
||||||
self.focus = Some(sibling);
|
self.focus = Some(sibling);
|
||||||
|
|
@ -157,7 +163,7 @@ impl Application for Example {
|
||||||
let focus = self.focus;
|
let focus = self.focus;
|
||||||
let total_panes = self.panes.len();
|
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 is_focused = focus == Some(id);
|
||||||
|
|
||||||
let pin_button = button(
|
let pin_button = button(
|
||||||
|
|
@ -178,7 +184,12 @@ impl Application for Example {
|
||||||
.spacing(5);
|
.spacing(5);
|
||||||
|
|
||||||
let title_bar = pane_grid::TitleBar::new(title)
|
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)
|
.padding(10)
|
||||||
.style(if is_focused {
|
.style(if is_focused {
|
||||||
style::title_bar_focused
|
style::title_bar_focused
|
||||||
|
|
@ -314,16 +325,35 @@ fn view_controls<'a>(
|
||||||
pane: pane_grid::Pane,
|
pane: pane_grid::Pane,
|
||||||
total_panes: usize,
|
total_panes: usize,
|
||||||
is_pinned: bool,
|
is_pinned: bool,
|
||||||
|
is_maximized: bool,
|
||||||
) -> Element<'a, Message> {
|
) -> 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)
|
.style(theme::Button::Destructive)
|
||||||
.padding(3);
|
.padding(3);
|
||||||
|
|
||||||
if total_panes > 1 && !is_pinned {
|
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 {
|
mod style {
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ use crate::{
|
||||||
/// let (mut state, _) = pane_grid::State::new(PaneState::SomePane);
|
/// let (mut state, _) = pane_grid::State::new(PaneState::SomePane);
|
||||||
///
|
///
|
||||||
/// let pane_grid =
|
/// let pane_grid =
|
||||||
/// PaneGrid::new(&state, |pane, state| {
|
/// PaneGrid::new(&state, |pane, state, is_maximized| {
|
||||||
/// pane_grid::Content::new(match state {
|
/// pane_grid::Content::new(match state {
|
||||||
/// PaneState::SomePane => text("This is some pane"),
|
/// PaneState::SomePane => text("This is some pane"),
|
||||||
/// PaneState::AnotherKindOfPane => text("This is another kind of pane"),
|
/// PaneState::AnotherKindOfPane => text("This is another kind of pane"),
|
||||||
|
|
@ -100,8 +100,7 @@ where
|
||||||
Renderer: crate::Renderer,
|
Renderer: crate::Renderer,
|
||||||
Renderer::Theme: StyleSheet + container::StyleSheet,
|
Renderer::Theme: StyleSheet + container::StyleSheet,
|
||||||
{
|
{
|
||||||
state: &'a state::Internal,
|
contents: Contents<'a, Content<'a, Message, Renderer>>,
|
||||||
elements: Vec<(Pane, Content<'a, Message, Renderer>)>,
|
|
||||||
width: Length,
|
width: Length,
|
||||||
height: Length,
|
height: Length,
|
||||||
spacing: u16,
|
spacing: u16,
|
||||||
|
|
@ -119,22 +118,35 @@ 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 contents = 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)))
|
Contents::Maximized(
|
||||||
.collect()
|
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 {
|
Self {
|
||||||
elements,
|
contents,
|
||||||
state: &state.internal,
|
|
||||||
width: Length::Fill,
|
width: Length::Fill,
|
||||||
height: Length::Fill,
|
height: Length::Fill,
|
||||||
spacing: 0,
|
spacing: 0,
|
||||||
|
|
@ -208,6 +220,12 @@ where
|
||||||
self.style = style.into();
|
self.style = style.into();
|
||||||
self
|
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>
|
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||||
|
|
@ -225,18 +243,25 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children(&self) -> Vec<Tree> {
|
fn children(&self) -> Vec<Tree> {
|
||||||
self.elements
|
self.contents
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_, content)| content.state())
|
.map(|(_, content)| content.state())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff(&self, tree: &mut Tree) {
|
fn diff(&self, tree: &mut Tree) {
|
||||||
tree.diff_children_custom(
|
match &self.contents {
|
||||||
&self.elements,
|
Contents::All(contents, _) => tree.diff_children_custom(
|
||||||
|state, (_, content)| content.diff(state),
|
contents,
|
||||||
|(_, content)| content.state(),
|
|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 {
|
fn width(&self) -> Length {
|
||||||
|
|
@ -255,12 +280,12 @@ where
|
||||||
layout(
|
layout(
|
||||||
renderer,
|
renderer,
|
||||||
limits,
|
limits,
|
||||||
self.state,
|
self.contents.layout(),
|
||||||
self.width,
|
self.width,
|
||||||
self.height,
|
self.height,
|
||||||
self.spacing,
|
self.spacing,
|
||||||
self.elements.iter().map(|(pane, content)| (*pane, content)),
|
self.contents.iter(),
|
||||||
|element, renderer, limits| element.layout(renderer, limits),
|
|content, renderer, limits| content.layout(renderer, limits),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -276,28 +301,34 @@ where
|
||||||
) -> event::Status {
|
) -> event::Status {
|
||||||
let action = tree.state.downcast_mut::<state::Action>();
|
let action = tree.state.downcast_mut::<state::Action>();
|
||||||
|
|
||||||
|
let on_drag = if self.drag_enabled() {
|
||||||
|
&self.on_drag
|
||||||
|
} else {
|
||||||
|
&None
|
||||||
|
};
|
||||||
|
|
||||||
let event_status = update(
|
let event_status = update(
|
||||||
action,
|
action,
|
||||||
self.state,
|
self.contents.layout(),
|
||||||
&event,
|
&event,
|
||||||
layout,
|
layout,
|
||||||
cursor_position,
|
cursor_position,
|
||||||
shell,
|
shell,
|
||||||
self.spacing,
|
self.spacing,
|
||||||
self.elements.iter().map(|(pane, content)| (*pane, content)),
|
self.contents.iter(),
|
||||||
&self.on_click,
|
&self.on_click,
|
||||||
&self.on_drag,
|
on_drag,
|
||||||
&self.on_resize,
|
&self.on_resize,
|
||||||
);
|
);
|
||||||
|
|
||||||
let picked_pane = action.picked_pane().map(|(pane, _)| pane);
|
let picked_pane = action.picked_pane().map(|(pane, _)| pane);
|
||||||
|
|
||||||
self.elements
|
self.contents
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.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,14 +354,14 @@ where
|
||||||
) -> mouse::Interaction {
|
) -> mouse::Interaction {
|
||||||
mouse_interaction(
|
mouse_interaction(
|
||||||
tree.state.downcast_ref(),
|
tree.state.downcast_ref(),
|
||||||
self.state,
|
self.contents.layout(),
|
||||||
layout,
|
layout,
|
||||||
cursor_position,
|
cursor_position,
|
||||||
self.spacing,
|
self.spacing,
|
||||||
self.on_resize.as_ref().map(|(leeway, _)| *leeway),
|
self.on_resize.as_ref().map(|(leeway, _)| *leeway),
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
self.elements
|
self.contents
|
||||||
.iter()
|
.iter()
|
||||||
.zip(&tree.children)
|
.zip(&tree.children)
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
|
|
@ -341,7 +372,7 @@ where
|
||||||
cursor_position,
|
cursor_position,
|
||||||
viewport,
|
viewport,
|
||||||
renderer,
|
renderer,
|
||||||
self.on_drag.is_some(),
|
self.drag_enabled(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.max()
|
.max()
|
||||||
|
|
@ -361,7 +392,7 @@ where
|
||||||
) {
|
) {
|
||||||
draw(
|
draw(
|
||||||
tree.state.downcast_ref(),
|
tree.state.downcast_ref(),
|
||||||
self.state,
|
self.contents.layout(),
|
||||||
layout,
|
layout,
|
||||||
cursor_position,
|
cursor_position,
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -371,10 +402,10 @@ where
|
||||||
self.spacing,
|
self.spacing,
|
||||||
self.on_resize.as_ref().map(|(leeway, _)| *leeway),
|
self.on_resize.as_ref().map(|(leeway, _)| *leeway),
|
||||||
self.style,
|
self.style,
|
||||||
self.elements
|
self.contents
|
||||||
.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,
|
||||||
|
|
@ -400,7 +431,7 @@ where
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||||
self.elements
|
self.contents
|
||||||
.iter()
|
.iter()
|
||||||
.zip(&mut tree.children)
|
.zip(&mut tree.children)
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
|
|
@ -429,24 +460,24 @@ 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,
|
||||||
elements: impl Iterator<Item = (Pane, T)>,
|
contents: impl Iterator<Item = (Pane, T)>,
|
||||||
layout_element: impl Fn(T, &Renderer, &layout::Limits) -> layout::Node,
|
layout_content: impl Fn(T, &Renderer, &layout::Limits) -> layout::Node,
|
||||||
) -> layout::Node {
|
) -> layout::Node {
|
||||||
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 = contents
|
||||||
.filter_map(|(pane, element)| {
|
.filter_map(|(pane, content)| {
|
||||||
let region = regions.get(&pane)?;
|
let region = regions.get(&pane)?;
|
||||||
let size = Size::new(region.width, region.height);
|
let size = Size::new(region.width, region.height);
|
||||||
|
|
||||||
let mut node = layout_element(
|
let mut node = layout_content(
|
||||||
element,
|
content,
|
||||||
renderer,
|
renderer,
|
||||||
&layout::Limits::new(size, size),
|
&layout::Limits::new(size, size),
|
||||||
);
|
);
|
||||||
|
|
@ -464,13 +495,13 @@ 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,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
spacing: u16,
|
spacing: u16,
|
||||||
elements: impl Iterator<Item = (Pane, T)>,
|
contents: impl Iterator<Item = (Pane, T)>,
|
||||||
on_click: &Option<Box<dyn Fn(Pane) -> Message + 'a>>,
|
on_click: &Option<Box<dyn Fn(Pane) -> Message + 'a>>,
|
||||||
on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
|
on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
|
||||||
on_resize: &Option<(u16, Box<dyn Fn(ResizeEvent) -> 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,
|
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),
|
||||||
);
|
);
|
||||||
|
|
@ -514,7 +545,7 @@ pub fn update<'a, Message, T: Draggable>(
|
||||||
layout,
|
layout,
|
||||||
cursor_position,
|
cursor_position,
|
||||||
shell,
|
shell,
|
||||||
elements,
|
contents,
|
||||||
on_click,
|
on_click,
|
||||||
on_drag,
|
on_drag,
|
||||||
);
|
);
|
||||||
|
|
@ -526,7 +557,7 @@ pub fn update<'a, Message, T: Draggable>(
|
||||||
layout,
|
layout,
|
||||||
cursor_position,
|
cursor_position,
|
||||||
shell,
|
shell,
|
||||||
elements,
|
contents,
|
||||||
on_click,
|
on_click,
|
||||||
on_drag,
|
on_drag,
|
||||||
);
|
);
|
||||||
|
|
@ -539,7 +570,7 @@ pub fn update<'a, Message, T: Draggable>(
|
||||||
| Event::Touch(touch::Event::FingerLost { .. }) => {
|
| Event::Touch(touch::Event::FingerLost { .. }) => {
|
||||||
if let Some((pane, _)) = action.picked_pane() {
|
if let Some((pane, _)) = action.picked_pane() {
|
||||||
if let Some(on_drag) = on_drag {
|
if let Some(on_drag) = on_drag {
|
||||||
let mut dropped_region = elements
|
let mut dropped_region = contents
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
.filter(|(_, layout)| {
|
.filter(|(_, layout)| {
|
||||||
layout.bounds().contains(cursor_position)
|
layout.bounds().contains(cursor_position)
|
||||||
|
|
@ -570,7 +601,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),
|
||||||
);
|
);
|
||||||
|
|
@ -609,13 +640,13 @@ fn click_pane<'a, Message, T>(
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
shell: &mut Shell<'_, Message>,
|
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_click: &Option<Box<dyn Fn(Pane) -> Message + 'a>>,
|
||||||
on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
|
on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
|
||||||
) where
|
) where
|
||||||
T: Draggable,
|
T: Draggable,
|
||||||
{
|
{
|
||||||
let mut clicked_region = elements
|
let mut clicked_region = contents
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
.filter(|(_, layout)| layout.bounds().contains(cursor_position));
|
.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`].
|
/// 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 +689,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 +718,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,
|
||||||
|
|
@ -697,7 +728,7 @@ pub fn draw<Renderer, T>(
|
||||||
spacing: u16,
|
spacing: u16,
|
||||||
resize_leeway: Option<u16>,
|
resize_leeway: Option<u16>,
|
||||||
style: <Renderer::Theme as StyleSheet>::Style,
|
style: <Renderer::Theme as StyleSheet>::Style,
|
||||||
elements: impl Iterator<Item = (Pane, T)>,
|
contents: impl Iterator<Item = (Pane, T)>,
|
||||||
draw_pane: impl Fn(
|
draw_pane: impl Fn(
|
||||||
T,
|
T,
|
||||||
&mut Renderer,
|
&mut Renderer,
|
||||||
|
|
@ -717,7 +748,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 +767,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(),
|
||||||
|
|
@ -759,7 +790,7 @@ pub fn draw<Renderer, T>(
|
||||||
|
|
||||||
let mut render_picked_pane = None;
|
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 {
|
match picked_pane {
|
||||||
Some((dragging, origin)) if id == dragging => {
|
Some((dragging, origin)) if id == dragging => {
|
||||||
render_picked_pane = Some((pane, origin, layout));
|
render_picked_pane = Some((pane, origin, layout));
|
||||||
|
|
@ -897,3 +928,49 @@ fn hovered_split<'a>(
|
||||||
})
|
})
|
||||||
.next()
|
.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(..))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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`].
|
||||||
|
|
@ -153,6 +160,7 @@ impl<T> State<T> {
|
||||||
node.split(new_split, axis, new_pane);
|
node.split(new_split, axis, new_pane);
|
||||||
|
|
||||||
let _ = self.panes.insert(new_pane, state);
|
let _ = self.panes.insert(new_pane, state);
|
||||||
|
let _ = self.maximized.take();
|
||||||
|
|
||||||
Some((new_pane, new_split))
|
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
|
/// 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.
|
||||||
|
///
|
||||||
|
/// [`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`].
|
/// The internal state of a [`PaneGrid`].
|
||||||
|
|
@ -226,11 +261,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 +341,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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue