Draft drag and drop support for PaneGrid
This commit is contained in:
parent
ed7c327b48
commit
eb070b9652
2 changed files with 196 additions and 46 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
input::{mouse, ButtonState},
|
input::{keyboard, mouse, ButtonState},
|
||||||
layout, Clipboard, Element, Event, Hasher, Layout, Length, Point,
|
layout, Clipboard, Element, Event, Hasher, Layout, Length, Point,
|
||||||
Rectangle, Size, Widget,
|
Rectangle, Size, Widget,
|
||||||
};
|
};
|
||||||
|
|
@ -12,6 +12,7 @@ pub struct PaneGrid<'a, Message, Renderer> {
|
||||||
elements: Vec<(Pane, Element<'a, Message, Renderer>)>,
|
elements: Vec<(Pane, Element<'a, Message, Renderer>)>,
|
||||||
width: Length,
|
width: Length,
|
||||||
height: Length,
|
height: Length,
|
||||||
|
on_drop: Option<Box<dyn Fn(Drop) -> Message>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> {
|
impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> {
|
||||||
|
|
@ -30,6 +31,7 @@ impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> {
|
||||||
elements,
|
elements,
|
||||||
width: Length::Fill,
|
width: Length::Fill,
|
||||||
height: Length::Fill,
|
height: Length::Fill,
|
||||||
|
on_drop: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,6 +50,17 @@ impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> {
|
||||||
self.height = height;
|
self.height = height;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn on_drop(mut self, f: impl Fn(Drop) -> Message + 'static) -> Self {
|
||||||
|
self.on_drop = Some(Box::new(f));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Drop {
|
||||||
|
pub pane: Pane,
|
||||||
|
pub target: Pane,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||||
|
|
@ -105,32 +118,76 @@ where
|
||||||
match event {
|
match event {
|
||||||
Event::Mouse(mouse::Event::Input {
|
Event::Mouse(mouse::Event::Input {
|
||||||
button: mouse::Button::Left,
|
button: mouse::Button::Left,
|
||||||
state: ButtonState::Pressed,
|
state,
|
||||||
}) => {
|
}) => match state {
|
||||||
let mut clicked_region =
|
ButtonState::Pressed => {
|
||||||
self.elements.iter().zip(layout.children()).filter(
|
let mut clicked_region =
|
||||||
|(_, layout)| layout.bounds().contains(cursor_position),
|
self.elements.iter().zip(layout.children()).filter(
|
||||||
);
|
|(_, layout)| {
|
||||||
|
layout.bounds().contains(cursor_position)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(((pane, _), _)) = clicked_region.next() {
|
if let Some(((pane, _), _)) = clicked_region.next() {
|
||||||
self.state.focused_pane = Some(*pane);
|
self.state.focus = if self.on_drop.is_some()
|
||||||
|
&& self.state.modifiers.alt
|
||||||
|
{
|
||||||
|
Some(Focus::Dragging(*pane))
|
||||||
|
} else {
|
||||||
|
Some(Focus::Idle(*pane))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
ButtonState::Released => {
|
||||||
|
if let Some(on_drop) = &self.on_drop {
|
||||||
|
if let Some(Focus::Dragging(pane)) = self.state.focus {
|
||||||
|
let mut dropped_region = self
|
||||||
|
.elements
|
||||||
|
.iter()
|
||||||
|
.zip(layout.children())
|
||||||
|
.filter(|(_, layout)| {
|
||||||
|
layout.bounds().contains(cursor_position)
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(((target, _), _)) =
|
||||||
|
dropped_region.next()
|
||||||
|
{
|
||||||
|
if pane != *target {
|
||||||
|
messages.push(on_drop(Drop {
|
||||||
|
pane,
|
||||||
|
target: *target,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state.focus = Some(Focus::Idle(pane));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Event::Keyboard(keyboard::Event::Input { modifiers, .. }) => {
|
||||||
|
self.state.modifiers = modifiers;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.elements.iter_mut().zip(layout.children()).for_each(
|
match self.state.focus {
|
||||||
|((_, pane), layout)| {
|
Some(Focus::Dragging(_)) => {}
|
||||||
pane.widget.on_event(
|
_ => {
|
||||||
event.clone(),
|
self.elements.iter_mut().zip(layout.children()).for_each(
|
||||||
layout,
|
|((_, pane), layout)| {
|
||||||
cursor_position,
|
pane.widget.on_event(
|
||||||
messages,
|
event.clone(),
|
||||||
renderer,
|
layout,
|
||||||
clipboard,
|
cursor_position,
|
||||||
)
|
messages,
|
||||||
},
|
renderer,
|
||||||
);
|
clipboard,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(
|
fn draw(
|
||||||
|
|
@ -140,7 +197,18 @@ where
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
) -> Renderer::Output {
|
) -> Renderer::Output {
|
||||||
renderer.draw(defaults, &self.elements, layout, cursor_position)
|
let dragging = match self.state.focus {
|
||||||
|
Some(Focus::Dragging(pane)) => Some(pane),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
renderer.draw(
|
||||||
|
defaults,
|
||||||
|
&self.elements,
|
||||||
|
dragging,
|
||||||
|
layout,
|
||||||
|
cursor_position,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_layout(&self, state: &mut Hasher) {
|
fn hash_layout(&self, state: &mut Hasher) {
|
||||||
|
|
@ -176,7 +244,14 @@ pub struct State<T> {
|
||||||
struct Internal {
|
struct Internal {
|
||||||
layout: Node,
|
layout: Node,
|
||||||
last_pane: usize,
|
last_pane: usize,
|
||||||
focused_pane: Option<Pane>,
|
focus: Option<Focus>,
|
||||||
|
modifiers: keyboard::ModifiersState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Focus {
|
||||||
|
Idle(Pane),
|
||||||
|
Dragging(Pane),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> State<T> {
|
impl<T> State<T> {
|
||||||
|
|
@ -192,7 +267,8 @@ impl<T> State<T> {
|
||||||
internal: Internal {
|
internal: Internal {
|
||||||
layout: Node::Pane(first_pane),
|
layout: Node::Pane(first_pane),
|
||||||
last_pane: 0,
|
last_pane: 0,
|
||||||
focused_pane: None,
|
focus: None,
|
||||||
|
modifiers: keyboard::ModifiersState::default(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
first_pane,
|
first_pane,
|
||||||
|
|
@ -216,11 +292,15 @@ impl<T> State<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focused_pane(&self) -> Option<Pane> {
|
pub fn focused_pane(&self) -> Option<Pane> {
|
||||||
self.internal.focused_pane
|
match self.internal.focus {
|
||||||
|
Some(Focus::Idle(pane)) => Some(pane),
|
||||||
|
Some(Focus::Dragging(_)) => None,
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focus(&mut self, pane: Pane) {
|
pub fn focus(&mut self, pane: Pane) {
|
||||||
self.internal.focused_pane = Some(pane);
|
self.internal.focus = Some(Focus::Idle(pane));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn split_vertically(&mut self, pane: &Pane, state: T) -> Option<Pane> {
|
pub fn split_vertically(&mut self, pane: &Pane, state: T) -> Option<Pane> {
|
||||||
|
|
@ -252,14 +332,27 @@ impl<T> State<T> {
|
||||||
node.split(kind, new_pane);
|
node.split(kind, new_pane);
|
||||||
|
|
||||||
let _ = self.panes.insert(new_pane, state);
|
let _ = self.panes.insert(new_pane, state);
|
||||||
self.internal.focused_pane = Some(new_pane);
|
self.internal.focus = Some(Focus::Idle(new_pane));
|
||||||
|
|
||||||
Some(new_pane)
|
Some(new_pane)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn swap(&mut self, a: &Pane, b: &Pane) {
|
||||||
|
self.internal.layout.update(&|node| match node {
|
||||||
|
Node::Split { .. } => {}
|
||||||
|
Node::Pane(pane) => {
|
||||||
|
if pane == a {
|
||||||
|
*node = Node::Pane(*b);
|
||||||
|
} else if pane == b {
|
||||||
|
*node = Node::Pane(*a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn close(&mut self, pane: &Pane) -> Option<T> {
|
pub fn close(&mut self, pane: &Pane) -> Option<T> {
|
||||||
if let Some(sibling) = self.internal.layout.remove(pane) {
|
if let Some(sibling) = self.internal.layout.remove(pane) {
|
||||||
self.internal.focused_pane = Some(sibling);
|
self.internal.focus = Some(Focus::Idle(sibling));
|
||||||
self.panes.remove(pane)
|
self.panes.remove(pane)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
@ -307,6 +400,18 @@ impl Node {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, f: &impl Fn(&mut Node)) {
|
||||||
|
match self {
|
||||||
|
Node::Split { a, b, .. } => {
|
||||||
|
a.update(f);
|
||||||
|
b.update(f);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
f(self);
|
||||||
|
}
|
||||||
|
|
||||||
fn remove(&mut self, pane: &Pane) -> Option<Pane> {
|
fn remove(&mut self, pane: &Pane) -> Option<Pane> {
|
||||||
match self {
|
match self {
|
||||||
Node::Split { a, b, .. } => {
|
Node::Split { a, b, .. } => {
|
||||||
|
|
@ -444,6 +549,7 @@ pub trait Renderer: crate::Renderer + Sized {
|
||||||
&mut self,
|
&mut self,
|
||||||
defaults: &Self::Defaults,
|
defaults: &Self::Defaults,
|
||||||
content: &[(Pane, Element<'_, Message, Self>)],
|
content: &[(Pane, Element<'_, Message, Self>)],
|
||||||
|
dragging: Option<Pane>,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
) -> Self::Output;
|
) -> Self::Output;
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,78 @@
|
||||||
use crate::{Primitive, Renderer};
|
use crate::{Primitive, Renderer};
|
||||||
use iced_native::{pane_grid, Element, Layout, MouseCursor, Point};
|
use iced_native::{
|
||||||
|
pane_grid::{self, Pane},
|
||||||
|
Element, Layout, MouseCursor, Point, Rectangle, Vector,
|
||||||
|
};
|
||||||
|
|
||||||
impl pane_grid::Renderer for Renderer {
|
impl pane_grid::Renderer for Renderer {
|
||||||
fn draw<Message>(
|
fn draw<Message>(
|
||||||
&mut self,
|
&mut self,
|
||||||
defaults: &Self::Defaults,
|
defaults: &Self::Defaults,
|
||||||
content: &[(pane_grid::Pane, Element<'_, Message, Self>)],
|
content: &[(Pane, Element<'_, Message, Self>)],
|
||||||
|
dragging: Option<Pane>,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
) -> Self::Output {
|
) -> Self::Output {
|
||||||
let mut mouse_cursor = MouseCursor::OutOfBounds;
|
let mut mouse_cursor = MouseCursor::OutOfBounds;
|
||||||
|
let mut dragged_pane = None;
|
||||||
|
|
||||||
|
let mut panes: Vec<_> = content
|
||||||
|
.iter()
|
||||||
|
.zip(layout.children())
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, ((id, pane), layout))| {
|
||||||
|
let (primitive, new_mouse_cursor) =
|
||||||
|
pane.draw(self, defaults, layout, cursor_position);
|
||||||
|
|
||||||
|
if new_mouse_cursor > mouse_cursor {
|
||||||
|
mouse_cursor = new_mouse_cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if Some(*id) == dragging {
|
||||||
|
dragged_pane = Some((i, layout));
|
||||||
|
}
|
||||||
|
|
||||||
|
primitive
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let primitives = if let Some((index, layout)) = dragged_pane {
|
||||||
|
let pane = panes.remove(index);
|
||||||
|
let bounds = layout.bounds();
|
||||||
|
|
||||||
|
// TODO: Fix once proper layering is implemented.
|
||||||
|
// This is a pretty hacky way to achieve layering.
|
||||||
|
let clip = Primitive::Clip {
|
||||||
|
bounds: Rectangle {
|
||||||
|
x: cursor_position.x - bounds.width / 2.0,
|
||||||
|
y: cursor_position.y - bounds.height / 2.0,
|
||||||
|
width: bounds.width + 0.5,
|
||||||
|
height: bounds.height + 0.5,
|
||||||
|
},
|
||||||
|
offset: Vector::new(0, 0),
|
||||||
|
content: Box::new(Primitive::Cached {
|
||||||
|
origin: Point::new(
|
||||||
|
cursor_position.x - bounds.x - bounds.width / 2.0,
|
||||||
|
cursor_position.y - bounds.y - bounds.height / 2.0,
|
||||||
|
),
|
||||||
|
cache: std::sync::Arc::new(pane),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
panes.push(clip);
|
||||||
|
|
||||||
|
panes
|
||||||
|
} else {
|
||||||
|
panes
|
||||||
|
};
|
||||||
|
|
||||||
(
|
(
|
||||||
Primitive::Group {
|
Primitive::Group { primitives },
|
||||||
primitives: content
|
if dragging.is_some() {
|
||||||
.iter()
|
MouseCursor::Grabbing
|
||||||
.zip(layout.children())
|
} else {
|
||||||
.map(|((_, pane), layout)| {
|
mouse_cursor
|
||||||
let (primitive, new_mouse_cursor) =
|
|
||||||
pane.draw(self, defaults, layout, cursor_position);
|
|
||||||
|
|
||||||
if new_mouse_cursor > mouse_cursor {
|
|
||||||
mouse_cursor = new_mouse_cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
primitive
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
},
|
},
|
||||||
mouse_cursor,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue