Add pane_grid functionality to split a pane with another pane

This commit is contained in:
Joao Freitas 2023-05-16 16:12:29 +01:00
parent 8e8b1e1eac
commit 99aa54cd88
No known key found for this signature in database
GPG key ID: 4FAF61C62BC37389
5 changed files with 198 additions and 7 deletions

View file

@ -108,8 +108,12 @@ impl Application for Example {
Message::Dragged(pane_grid::DragEvent::Dropped {
pane,
target,
region,
}) => {
self.panes.swap(&pane, &target);
if let Some(state) = self.panes.get(&pane) {
let pane = (*state, &pane);
self.panes.split_with(&target, pane, region);
}
}
Message::Dragged(_) => {}
Message::TogglePin(pane) => {
@ -255,6 +259,7 @@ fn handle_hotkey(key_code: keyboard::KeyCode) -> Option<Message> {
}
}
#[derive(Clone, Copy)]
struct Pane {
id: usize,
pub is_pinned: bool,

View file

@ -1,11 +1,14 @@
//! Change the appearance of a pane grid.
use iced_core::Color;
use iced_core::{Background, Color};
/// A set of rules that dictate the style of a container.
pub trait StyleSheet {
/// The supported style of the [`StyleSheet`].
type Style: Default;
/// The [`Region`] to draw when a pane is hovered.
fn hovered_region(&self, style: &Self::Style) -> Region;
/// The [`Line`] to draw when a split is picked.
fn picked_split(&self, style: &Self::Style) -> Option<Line>;
@ -13,6 +16,19 @@ pub trait StyleSheet {
fn hovered_split(&self, style: &Self::Style) -> Option<Line>;
}
/// The appearance of the hovered region of a pane grid.
#[derive(Debug, Clone, Copy)]
pub struct Region {
/// The [`Background`] of the hovered pane region.
pub background: Background,
/// The border width of the hovered pane region.
pub border_width: f32,
/// The border [`Color`] of the hovered pane region.
pub border_color: Color,
/// The border radius of the hovered pane region.
pub border_radius: f32,
}
/// A line.
///
/// It is normally used to define the highlight of something, like a split.

View file

@ -703,6 +703,25 @@ pub enum PaneGrid {
impl pane_grid::StyleSheet for Theme {
type Style = PaneGrid;
fn hovered_region(&self, style: &Self::Style) -> pane_grid::Region {
match style {
PaneGrid::Default => {
let palette = self.extended_palette();
pane_grid::Region {
background: Background::Color(Color {
a: 0.5,
..palette.primary.base.color
}),
border_width: 2.0,
border_color: palette.primary.strong.color,
border_radius: 0.0,
}
}
PaneGrid::Custom(custom) => custom.hovered_region(self),
}
}
fn picked_split(&self, style: &Self::Style) -> Option<pane_grid::Line> {
match style {
PaneGrid::Default => {

View file

@ -594,13 +594,18 @@ pub fn update<'a, Message, T: Draggable>(
if let Some(on_drag) = on_drag {
let mut dropped_region = contents
.zip(layout.children())
.filter(|(_, layout)| {
layout.bounds().contains(cursor_position)
.filter_map(|(target, layout)| {
layout_region(layout, cursor_position)
.map(|region| (target, region))
});
let event = match dropped_region.next() {
Some(((target, _), _)) if pane != target => {
DragEvent::Dropped { pane, target }
Some(((target, _), region)) if pane != target => {
DragEvent::Dropped {
pane,
target,
region,
}
}
_ => DragEvent::Canceled { pane },
};
@ -657,6 +662,28 @@ pub fn update<'a, Message, T: Draggable>(
event_status
}
fn layout_region(layout: Layout<'_>, cursor_position: Point) -> Option<Region> {
let bounds = layout.bounds();
if !bounds.contains(cursor_position) {
return None;
}
let region = if cursor_position.x < (bounds.x + bounds.width / 3.0) {
Region::Left
} else if cursor_position.x > (bounds.x + 2.0 * bounds.width / 3.0) {
Region::Right
} else if cursor_position.y < (bounds.y + bounds.height / 3.0) {
Region::Top
} else if cursor_position.y > (bounds.y + 2.0 * bounds.height / 3.0) {
Region::Bottom
} else {
Region::Center
};
Some(region)
}
fn click_pane<'a, Message, T>(
action: &mut state::Action,
layout: Layout<'_>,
@ -810,6 +837,36 @@ pub fn draw<Renderer, T>(
Some((dragging, origin)) if id == dragging => {
render_picked_pane = Some((pane, origin, layout));
}
Some((dragging, _)) if id != dragging => {
draw_pane(
pane,
renderer,
default_style,
layout,
pane_cursor_position,
viewport,
);
if picked_pane.is_some() {
if let Some(region) = layout_region(layout, cursor_position)
{
let bounds = layout_region_bounds(layout, region);
let hovered_region_style = theme.hovered_region(style);
renderer.fill_quad(
renderer::Quad {
bounds,
border_radius: hovered_region_style
.border_radius
.into(),
border_width: hovered_region_style.border_width,
border_color: hovered_region_style.border_color,
},
theme.hovered_region(style).background,
);
}
}
}
_ => {
draw_pane(
pane,
@ -884,6 +941,32 @@ pub fn draw<Renderer, T>(
}
}
fn layout_region_bounds(layout: Layout<'_>, region: Region) -> Rectangle {
let bounds = layout.bounds();
match region {
Region::Center => bounds,
Region::Top => Rectangle {
height: bounds.height / 2.0,
..bounds
},
Region::Left => Rectangle {
width: bounds.width / 2.0,
..bounds
},
Region::Right => Rectangle {
x: bounds.x + bounds.width / 2.0,
width: bounds.width / 2.0,
..bounds
},
Region::Bottom => Rectangle {
y: bounds.y + bounds.height / 2.0,
height: bounds.height / 2.0,
..bounds
},
}
}
/// An event produced during a drag and drop interaction of a [`PaneGrid`].
#[derive(Debug, Clone, Copy)]
pub enum DragEvent {
@ -900,6 +983,9 @@ pub enum DragEvent {
/// The [`Pane`] where the picked one was dropped on.
target: Pane,
/// The [`Region`] of the target [`Pane`] where the picked one was dropped on.
region: Region,
},
/// A [`Pane`] was picked and then dropped outside of other [`Pane`]
@ -910,6 +996,22 @@ pub enum DragEvent {
},
}
/// The region of a [`Pane`].
#[derive(Debug, Clone, Copy, Default)]
pub enum Region {
/// Center region.
#[default]
Center,
/// Top region.
Top,
/// Left region.
Left,
/// Right region.
Right,
/// Bottom region.
Bottom,
}
/// An event produced during a resize interaction of a [`PaneGrid`].
#[derive(Debug, Clone, Copy)]
pub struct ResizeEvent {

View file

@ -2,7 +2,9 @@
//!
//! [`PaneGrid`]: crate::widget::PaneGrid
use crate::core::{Point, Size};
use crate::pane_grid::{Axis, Configuration, Direction, Node, Pane, Split};
use crate::pane_grid::{
Axis, Configuration, Direction, Node, Pane, Region, Split,
};
use std::collections::HashMap;
@ -165,6 +167,53 @@ impl<T> State<T> {
Some((new_pane, new_split))
}
/// Split a target [`Pane`] with a given [`Pane`] on a given [`Region`].
///
/// Panes will be swapped by default for [`Region::Center`].
pub fn split_with(
&mut self,
target: &Pane,
pane: (T, &Pane),
region: Region,
) {
match region {
Region::Center => {
let (_, pane) = pane;
self.swap(pane, target);
}
Region::Top => {
self.split_and_swap(Axis::Horizontal, target, pane, true)
}
Region::Bottom => {
self.split_and_swap(Axis::Horizontal, target, pane, false)
}
Region::Left => {
self.split_and_swap(Axis::Vertical, target, pane, true)
}
Region::Right => {
self.split_and_swap(Axis::Vertical, target, pane, false)
}
}
}
fn split_and_swap(
&mut self,
axis: Axis,
target: &Pane,
pane: (T, &Pane),
invert: bool,
) {
let (state, pane) = pane;
if let Some((new_pane, _)) = self.split(axis, target, state) {
if invert {
self.swap(target, &new_pane);
}
let _ = self.close(pane);
}
}
/// Swaps the position of the provided panes in the [`State`].
///
/// If you want to swap panes on drag and drop in your [`PaneGrid`], you