Add pane_grid functionality to split a pane with another pane
This commit is contained in:
parent
8e8b1e1eac
commit
99aa54cd88
5 changed files with 198 additions and 7 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue