summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón <hector0193@gmail.com>2023-05-19 17:16:22 +0200
committerLibravatar GitHub <noreply@github.com>2023-05-19 17:16:22 +0200
commit640e13943c04754ef74e11db470b6c9470640623 (patch)
treef2163bb2cbe7e26ecdd78035203181e42d8a19cb
parentcc5d11f1a6fca90ea57e3fb3a69587c65281b6b9 (diff)
parent9b5f32ee403c8b0730e3bac2b48aab6b87d7b653 (diff)
downloadiced-640e13943c04754ef74e11db470b6c9470640623.tar.gz
iced-640e13943c04754ef74e11db470b6c9470640623.tar.bz2
iced-640e13943c04754ef74e11db470b6c9470640623.zip
Merge pull request #1856 from jhff/pane_grid_split_with_dragged_pane
[Feature] Enhance PaneGrid to split panes by drag & drop
Diffstat (limited to '')
-rw-r--r--examples/pane_grid/src/main.rs4
-rw-r--r--style/src/pane_grid.rs38
-rw-r--r--style/src/theme.rs19
-rw-r--r--widget/src/pane_grid.rs112
-rw-r--r--widget/src/pane_grid/state.rs41
5 files changed, 196 insertions, 18 deletions
diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs
index dfb80853..2ffdcc69 100644
--- a/examples/pane_grid/src/main.rs
+++ b/examples/pane_grid/src/main.rs
@@ -108,8 +108,9 @@ impl Application for Example {
Message::Dragged(pane_grid::DragEvent::Dropped {
pane,
target,
+ region,
}) => {
- self.panes.swap(&pane, &target);
+ self.panes.split_with(&target, &pane, region);
}
Message::Dragged(_) => {}
Message::TogglePin(pane) => {
@@ -255,6 +256,7 @@ fn handle_hotkey(key_code: keyboard::KeyCode) -> Option<Message> {
}
}
+#[derive(Clone, Copy)]
struct Pane {
id: usize,
pub is_pinned: bool,
diff --git a/style/src/pane_grid.rs b/style/src/pane_grid.rs
index fd8fc05f..c1002725 100644
--- a/style/src/pane_grid.rs
+++ b/style/src/pane_grid.rs
@@ -1,16 +1,17 @@
//! 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 [`Line`] to draw when a split is picked.
- fn picked_split(&self, style: &Self::Style) -> Option<Line>;
-
- /// The [`Line`] to draw when a split is hovered.
- 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 Appearance {
+ /// 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.
@@ -24,3 +25,18 @@ pub struct Line {
/// The width of the [`Line`].
pub width: f32,
}
+
+/// 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) -> Appearance;
+
+ /// The [`Line`] to draw when a split is picked.
+ fn picked_split(&self, style: &Self::Style) -> Option<Line>;
+
+ /// The [`Line`] to draw when a split is hovered.
+ fn hovered_split(&self, style: &Self::Style) -> Option<Line>;
+}
diff --git a/style/src/theme.rs b/style/src/theme.rs
index 477bd27b..6299975d 100644
--- a/style/src/theme.rs
+++ b/style/src/theme.rs
@@ -715,6 +715,25 @@ pub enum PaneGrid {
impl pane_grid::StyleSheet for Theme {
type Style = PaneGrid;
+ fn hovered_region(&self, style: &Self::Style) -> pane_grid::Appearance {
+ match style {
+ PaneGrid::Default => {
+ let palette = self.extended_palette();
+
+ pane_grid::Appearance {
+ 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 => {
diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs
index 67145e8e..e6ffb1d6 100644
--- a/widget/src/pane_grid.rs
+++ b/widget/src/pane_grid.rs
@@ -30,7 +30,7 @@ pub use split::Split;
pub use state::State;
pub use title_bar::TitleBar;
-pub use crate::style::pane_grid::{Line, StyleSheet};
+pub use crate::style::pane_grid::{Appearance, Line, StyleSheet};
use crate::container;
use crate::core::event::{self, Event};
@@ -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 {
diff --git a/widget/src/pane_grid/state.rs b/widget/src/pane_grid/state.rs
index a6e2ec7f..1f034ca3 100644
--- a/widget/src/pane_grid/state.rs
+++ b/widget/src/pane_grid/state.rs
@@ -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,43 @@ 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: &Pane, region: Region) {
+ match region {
+ Region::Center => 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: &Pane,
+ swap: bool,
+ ) {
+ if let Some((state, _)) = self.close(pane) {
+ if let Some((new_pane, _)) = self.split(axis, target, state) {
+ if swap {
+ self.swap(target, &new_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