tiles: Ensure to response correct panel on drag or resize. (#1422)
Use `item_id` to store the dragging and resizing item.
This commit is contained in:
parent
08b5013b36
commit
04fd5e1511
1 changed files with 230 additions and 316 deletions
|
|
@ -15,14 +15,13 @@ use super::{
|
||||||
DockArea, Panel, PanelEvent, PanelInfo, PanelState, PanelView, StackPanel, TabPanel, TileMeta,
|
DockArea, Panel, PanelEvent, PanelInfo, PanelState, PanelView, StackPanel, TabPanel, TileMeta,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, canvas, div, point, px, size, AnyElement, App, AppContext, Bounds, Context,
|
actions, canvas, div, px, size, AnyElement, App, AppContext, Bounds, Context, DismissEvent,
|
||||||
DismissEvent, DragMoveEvent, Empty, EntityId, EventEmitter, FocusHandle, Focusable, Half,
|
DragMoveEvent, Empty, EntityId, EventEmitter, FocusHandle, Focusable, InteractiveElement,
|
||||||
InteractiveElement, IntoElement, MouseButton, MouseDownEvent, MouseUpEvent, ParentElement,
|
IntoElement, MouseButton, MouseDownEvent, MouseUpEvent, ParentElement, Pixels, Point, Render,
|
||||||
Pixels, Point, Render, ScrollHandle, Size, StatefulInteractiveElement, Styled, WeakEntity,
|
ScrollHandle, Size, StatefulInteractiveElement, Styled, WeakEntity, Window,
|
||||||
Window,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
actions!(tiles, [Undo, Redo,]);
|
actions!(tiles, [Undo, Redo]);
|
||||||
|
|
||||||
const MINIMUM_SIZE: Size<Pixels> = size(px(100.), px(100.));
|
const MINIMUM_SIZE: Size<Pixels> = size(px(100.), px(100.));
|
||||||
const DRAG_BAR_HEIGHT: Pixels = px(30.);
|
const DRAG_BAR_HEIGHT: Pixels = px(30.);
|
||||||
|
|
@ -84,6 +83,7 @@ struct ResizeDrag {
|
||||||
/// TileItem is a moveable and resizable panel that can be added to a Tiles view.
|
/// TileItem is a moveable and resizable panel that can be added to a Tiles view.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TileItem {
|
pub struct TileItem {
|
||||||
|
id: EntityId,
|
||||||
pub(crate) panel: Arc<dyn PanelView>,
|
pub(crate) panel: Arc<dyn PanelView>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
z_index: usize,
|
z_index: usize,
|
||||||
|
|
@ -101,6 +101,7 @@ impl Debug for TileItem {
|
||||||
impl TileItem {
|
impl TileItem {
|
||||||
pub fn new(panel: Arc<dyn PanelView>, bounds: Bounds<Pixels>) -> Self {
|
pub fn new(panel: Arc<dyn PanelView>, bounds: Bounds<Pixels>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
id: panel.view().entity_id(),
|
||||||
panel,
|
panel,
|
||||||
bounds,
|
bounds,
|
||||||
z_index: 0,
|
z_index: 0,
|
||||||
|
|
@ -130,10 +131,10 @@ impl AnyDrag {
|
||||||
pub struct Tiles {
|
pub struct Tiles {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
pub(crate) panels: Vec<TileItem>,
|
pub(crate) panels: Vec<TileItem>,
|
||||||
dragging_index: Option<usize>,
|
dragging_id: Option<EntityId>,
|
||||||
dragging_initial_mouse: Point<Pixels>,
|
dragging_initial_mouse: Point<Pixels>,
|
||||||
dragging_initial_bounds: Bounds<Pixels>,
|
dragging_initial_bounds: Bounds<Pixels>,
|
||||||
resizing_index: Option<usize>,
|
resizing_id: Option<EntityId>,
|
||||||
resizing_drag_data: Option<ResizeDrag>,
|
resizing_drag_data: Option<ResizeDrag>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
history: History<TileChange>,
|
history: History<TileChange>,
|
||||||
|
|
@ -184,10 +185,10 @@ impl Tiles {
|
||||||
Self {
|
Self {
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
panels: vec![],
|
panels: vec![],
|
||||||
dragging_index: None,
|
dragging_id: None,
|
||||||
dragging_initial_mouse: Point::default(),
|
dragging_initial_mouse: Point::default(),
|
||||||
dragging_initial_bounds: Bounds::default(),
|
dragging_initial_bounds: Bounds::default(),
|
||||||
resizing_index: None,
|
resizing_id: None,
|
||||||
resizing_drag_data: None,
|
resizing_drag_data: None,
|
||||||
bounds: Bounds::default(),
|
bounds: Bounds::default(),
|
||||||
history: History::new().group_interval(std::time::Duration::from_millis(100)),
|
history: History::new().group_interval(std::time::Duration::from_millis(100)),
|
||||||
|
|
@ -208,48 +209,35 @@ impl Tiles {
|
||||||
|
|
||||||
/// Return the index of the panel.
|
/// Return the index of the panel.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn index_of(&self, panel: Arc<dyn PanelView>) -> Option<usize> {
|
pub(crate) fn index_of(&self, id: &EntityId) -> Option<usize> {
|
||||||
self.panels.iter().position(|p| &p.panel == &panel)
|
self.panels.iter().position(|p| &p.id == id)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn panel(&self, id: &EntityId) -> Option<&TileItem> {
|
||||||
|
self.panels.iter().find(|p| &p.id == id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove panel from the children.
|
/// Remove panel from the children.
|
||||||
pub fn remove(&mut self, panel: Arc<dyn PanelView>, _: &mut Window, cx: &mut Context<Self>) {
|
pub fn remove(&mut self, panel: Arc<dyn PanelView>, _: &mut Window, cx: &mut Context<Self>) {
|
||||||
if let Some(ix) = self.index_of(panel.clone()) {
|
if let Some(ix) = self.index_of(&panel.panel_id(cx)) {
|
||||||
self.panels.remove(ix);
|
self.panels.remove(ix);
|
||||||
|
|
||||||
cx.emit(PanelEvent::LayoutChanged);
|
cx.emit(PanelEvent::LayoutChanged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_initial_position(
|
|
||||||
&mut self,
|
|
||||||
position: Point<Pixels>,
|
|
||||||
_: &mut Window,
|
|
||||||
cx: &mut Context<'_, Self>,
|
|
||||||
) {
|
|
||||||
let Some((index, item)) = self.find_at_position(position) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let inner_pos = position - self.bounds.origin;
|
|
||||||
let bounds = item.bounds;
|
|
||||||
self.dragging_index = Some(index);
|
|
||||||
self.dragging_initial_mouse = inner_pos;
|
|
||||||
self.dragging_initial_bounds = bounds;
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_position(
|
fn update_position(
|
||||||
&mut self,
|
&mut self,
|
||||||
mouse_position: Point<Pixels>,
|
mouse_position: Point<Pixels>,
|
||||||
_: &mut Window,
|
_: &mut Window,
|
||||||
cx: &mut Context<'_, Self>,
|
cx: &mut Context<'_, Self>,
|
||||||
) {
|
) {
|
||||||
let Some(index) = self.dragging_index else {
|
let Some(dragging_id) = self.dragging_id else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(item) = self.panels.get_mut(index) else {
|
let Some(item) = self.panels.iter_mut().find(|p| p.id == dragging_id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -288,19 +276,6 @@ impl Tiles {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_resizing_drag(
|
|
||||||
&mut self,
|
|
||||||
drag_data: ResizeDrag,
|
|
||||||
_: &mut Window,
|
|
||||||
cx: &mut Context<'_, Self>,
|
|
||||||
) {
|
|
||||||
if let Some((index, _item)) = self.find_at_position(drag_data.last_position) {
|
|
||||||
self.resizing_index = Some(index);
|
|
||||||
self.resizing_drag_data = Some(drag_data);
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resize(
|
fn resize(
|
||||||
&mut self,
|
&mut self,
|
||||||
new_x: Option<Pixels>,
|
new_x: Option<Pixels>,
|
||||||
|
|
@ -310,58 +285,61 @@ impl Tiles {
|
||||||
_: &mut Window,
|
_: &mut Window,
|
||||||
cx: &mut Context<'_, Self>,
|
cx: &mut Context<'_, Self>,
|
||||||
) {
|
) {
|
||||||
if let Some(index) = self.resizing_index {
|
let Some(resizing_id) = self.resizing_id else {
|
||||||
if let Some(item) = self.panels.get_mut(index) {
|
return;
|
||||||
let previous_bounds = item.bounds;
|
};
|
||||||
let final_x = if let Some(x) = new_x {
|
let Some(item) = self.panels.iter_mut().find(|item| item.id == resizing_id) else {
|
||||||
round_to_nearest_ten(x, cx)
|
return;
|
||||||
} else {
|
};
|
||||||
previous_bounds.origin.x
|
|
||||||
};
|
|
||||||
let final_y = if let Some(y) = new_y {
|
|
||||||
round_to_nearest_ten(y, cx)
|
|
||||||
} else {
|
|
||||||
previous_bounds.origin.y
|
|
||||||
};
|
|
||||||
let final_width = if let Some(width) = new_width {
|
|
||||||
round_to_nearest_ten(width, cx)
|
|
||||||
} else {
|
|
||||||
previous_bounds.size.width
|
|
||||||
};
|
|
||||||
|
|
||||||
let final_height = if let Some(height) = new_height {
|
let previous_bounds = item.bounds;
|
||||||
round_to_nearest_ten(height, cx)
|
let final_x = if let Some(x) = new_x {
|
||||||
} else {
|
round_to_nearest_ten(x, cx)
|
||||||
previous_bounds.size.height
|
} else {
|
||||||
};
|
previous_bounds.origin.x
|
||||||
|
};
|
||||||
|
let final_y = if let Some(y) = new_y {
|
||||||
|
round_to_nearest_ten(y, cx)
|
||||||
|
} else {
|
||||||
|
previous_bounds.origin.y
|
||||||
|
};
|
||||||
|
let final_width = if let Some(width) = new_width {
|
||||||
|
round_to_nearest_ten(width, cx)
|
||||||
|
} else {
|
||||||
|
previous_bounds.size.width
|
||||||
|
};
|
||||||
|
|
||||||
// Only push to history if size has changed
|
let final_height = if let Some(height) = new_height {
|
||||||
if final_width != item.bounds.size.width
|
round_to_nearest_ten(height, cx)
|
||||||
|| final_height != item.bounds.size.height
|
} else {
|
||||||
|| final_x != item.bounds.origin.x
|
previous_bounds.size.height
|
||||||
|| final_y != item.bounds.origin.y
|
};
|
||||||
{
|
|
||||||
item.bounds.origin.x = final_x;
|
|
||||||
item.bounds.origin.y = final_y;
|
|
||||||
item.bounds.size.width = final_width;
|
|
||||||
item.bounds.size.height = final_height;
|
|
||||||
|
|
||||||
// Only push if not during history operations
|
// Only push to history if size has changed
|
||||||
if !self.history.ignore {
|
if final_width != item.bounds.size.width
|
||||||
self.history.push(TileChange {
|
|| final_height != item.bounds.size.height
|
||||||
tile_id: item.panel.view().entity_id(),
|
|| final_x != item.bounds.origin.x
|
||||||
old_bounds: Some(previous_bounds),
|
|| final_y != item.bounds.origin.y
|
||||||
new_bounds: Some(item.bounds),
|
{
|
||||||
old_order: None,
|
item.bounds.origin.x = final_x;
|
||||||
new_order: None,
|
item.bounds.origin.y = final_y;
|
||||||
version: 0,
|
item.bounds.size.width = final_width;
|
||||||
});
|
item.bounds.size.height = final_height;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.notify();
|
// Only push if not during history operations
|
||||||
|
if !self.history.ignore {
|
||||||
|
self.history.push(TileChange {
|
||||||
|
tile_id: item.panel.view().entity_id(),
|
||||||
|
old_bounds: Some(previous_bounds),
|
||||||
|
new_bounds: Some(item.bounds),
|
||||||
|
old_order: None,
|
||||||
|
new_order: None,
|
||||||
|
version: 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_item(
|
pub fn add_item(
|
||||||
|
|
@ -398,56 +376,38 @@ impl Tiles {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the panel at a given position, considering z-index
|
|
||||||
fn find_at_position(&self, position: Point<Pixels>) -> Option<(usize, &TileItem)> {
|
|
||||||
let inner_pos = position - self.bounds.origin;
|
|
||||||
let mut panels_with_indices: Vec<(usize, &TileItem)> =
|
|
||||||
self.panels.iter().enumerate().collect();
|
|
||||||
|
|
||||||
panels_with_indices
|
|
||||||
.sort_by(|a, b| b.1.z_index.cmp(&a.1.z_index).then_with(|| b.0.cmp(&a.0)));
|
|
||||||
|
|
||||||
for (index, item) in panels_with_indices {
|
|
||||||
let extended_bounds = Bounds::new(
|
|
||||||
item.bounds.origin,
|
|
||||||
item.bounds.size + size(HANDLE_SIZE, HANDLE_SIZE) / 2.0,
|
|
||||||
);
|
|
||||||
if extended_bounds.contains(&inner_pos) {
|
|
||||||
return Some((index, item));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn reset_current_index(&mut self) {
|
fn reset_current_index(&mut self) {
|
||||||
self.dragging_index = None;
|
self.dragging_id = None;
|
||||||
self.resizing_index = None;
|
self.resizing_id = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bring the panel of target_index to front, returns (old_index, new_index) if successful
|
/// Bring the panel of target_index to front, returns (old_index, new_index) if successful
|
||||||
fn bring_to_front(
|
fn bring_to_front(
|
||||||
&mut self,
|
&mut self,
|
||||||
target_index: Option<usize>,
|
target_id: Option<EntityId>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Option<(usize, usize)> {
|
) -> Option<EntityId> {
|
||||||
if let Some(old_index) = target_index {
|
let Some(old_id) = target_id else {
|
||||||
if old_index < self.panels.len() {
|
return None;
|
||||||
let item = self.panels.remove(old_index);
|
};
|
||||||
self.panels.push(item);
|
|
||||||
let new_index = self.panels.len() - 1;
|
let old_ix = self.panels.iter().position(|item| item.id == old_id)?;
|
||||||
self.history.push(TileChange {
|
if old_ix < self.panels.len() {
|
||||||
tile_id: self.panels[new_index].panel.view().entity_id(),
|
let item = self.panels.remove(old_ix);
|
||||||
old_bounds: None,
|
self.panels.push(item);
|
||||||
new_bounds: None,
|
let new_ix = self.panels.len() - 1;
|
||||||
old_order: Some(old_index),
|
let new_id = self.panels[new_ix].id;
|
||||||
new_order: Some(new_index),
|
self.history.push(TileChange {
|
||||||
version: 0,
|
tile_id: new_id,
|
||||||
});
|
old_bounds: None,
|
||||||
cx.notify();
|
new_bounds: None,
|
||||||
return Some((old_index, new_index));
|
old_order: Some(old_ix),
|
||||||
}
|
new_order: Some(new_ix),
|
||||||
|
version: 0,
|
||||||
|
});
|
||||||
|
cx.notify();
|
||||||
|
return Some(new_id);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -526,33 +486,15 @@ impl Tiles {
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
entity_id: EntityId,
|
entity_id: EntityId,
|
||||||
item: &TileItem,
|
item: &TileItem,
|
||||||
is_occluded: impl Fn(&Bounds<Pixels>) -> bool,
|
|
||||||
) -> Vec<AnyElement> {
|
) -> Vec<AnyElement> {
|
||||||
let panel_bounds = item.bounds;
|
let item_id = item.id;
|
||||||
let right_handle_bounds = Bounds::new(
|
let item_bounds = item.bounds;
|
||||||
panel_bounds.origin + point(panel_bounds.size.width - HANDLE_SIZE, px(0.0)),
|
|
||||||
size(HANDLE_SIZE, panel_bounds.size.height),
|
|
||||||
);
|
|
||||||
|
|
||||||
let bottom_handle_bounds = Bounds::new(
|
|
||||||
panel_bounds.origin + point(px(0.0), panel_bounds.size.height - HANDLE_SIZE.half()),
|
|
||||||
size(panel_bounds.size.width, HANDLE_SIZE.half()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let corner_handle_bounds = Bounds::new(
|
|
||||||
panel_bounds.origin
|
|
||||||
+ point(
|
|
||||||
panel_bounds.size.width - HANDLE_SIZE.half(),
|
|
||||||
panel_bounds.size.height - HANDLE_SIZE.half(),
|
|
||||||
),
|
|
||||||
size(HANDLE_SIZE.half(), HANDLE_SIZE.half()),
|
|
||||||
);
|
|
||||||
let handle_offset = -HANDLE_SIZE + px(1.);
|
let handle_offset = -HANDLE_SIZE + px(1.);
|
||||||
|
|
||||||
let mut elements = Vec::new();
|
let mut elements = Vec::new();
|
||||||
|
|
||||||
// Left resize handle
|
// Left resize handle
|
||||||
elements.push(if !is_occluded(&right_handle_bounds) {
|
elements.push(
|
||||||
div()
|
div()
|
||||||
.id("left-resize-handle")
|
.id("left-resize-handle")
|
||||||
.cursor_ew_resize()
|
.cursor_ew_resize()
|
||||||
|
|
@ -560,22 +502,19 @@ impl Tiles {
|
||||||
.top_0()
|
.top_0()
|
||||||
.left(handle_offset)
|
.left(handle_offset)
|
||||||
.w(HANDLE_SIZE)
|
.w(HANDLE_SIZE)
|
||||||
.h(panel_bounds.size.height)
|
.h(item_bounds.size.height)
|
||||||
.on_mouse_down(
|
.on_mouse_down(
|
||||||
MouseButton::Left,
|
MouseButton::Left,
|
||||||
cx.listener({
|
cx.listener({
|
||||||
move |this, event: &MouseDownEvent, window, cx| {
|
move |this, event: &MouseDownEvent, window, cx| {
|
||||||
let last_position = event.position;
|
this.on_resize_handle_mouse_down(
|
||||||
let drag_data = ResizeDrag {
|
ResizeSide::Left,
|
||||||
side: ResizeSide::Left,
|
item_id,
|
||||||
last_position,
|
item_bounds,
|
||||||
last_bounds: panel_bounds,
|
event,
|
||||||
};
|
window,
|
||||||
this.update_resizing_drag(drag_data, window, cx);
|
cx,
|
||||||
if let Some((_, new_ix)) = this.bring_to_front(this.resizing_index, cx)
|
);
|
||||||
{
|
|
||||||
this.resizing_index = Some(new_ix);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -607,13 +546,11 @@ impl Tiles {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.into_any_element()
|
.into_any_element(),
|
||||||
} else {
|
);
|
||||||
div().into_any_element()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Right resize handle
|
// Right resize handle
|
||||||
elements.push(if !is_occluded(&right_handle_bounds) {
|
elements.push(
|
||||||
div()
|
div()
|
||||||
.id("right-resize-handle")
|
.id("right-resize-handle")
|
||||||
.cursor_ew_resize()
|
.cursor_ew_resize()
|
||||||
|
|
@ -621,22 +558,19 @@ impl Tiles {
|
||||||
.top_0()
|
.top_0()
|
||||||
.right(handle_offset)
|
.right(handle_offset)
|
||||||
.w(HANDLE_SIZE)
|
.w(HANDLE_SIZE)
|
||||||
.h(panel_bounds.size.height)
|
.h(item_bounds.size.height)
|
||||||
.on_mouse_down(
|
.on_mouse_down(
|
||||||
MouseButton::Left,
|
MouseButton::Left,
|
||||||
cx.listener({
|
cx.listener({
|
||||||
move |this, event: &MouseDownEvent, window, cx| {
|
move |this, event: &MouseDownEvent, window, cx| {
|
||||||
let last_position = event.position;
|
this.on_resize_handle_mouse_down(
|
||||||
let drag_data = ResizeDrag {
|
ResizeSide::Right,
|
||||||
side: ResizeSide::Right,
|
item_id,
|
||||||
last_position,
|
item_bounds,
|
||||||
last_bounds: panel_bounds,
|
event,
|
||||||
};
|
window,
|
||||||
this.update_resizing_drag(drag_data, window, cx);
|
cx,
|
||||||
if let Some((_, new_ix)) = this.bring_to_front(this.resizing_index, cx)
|
);
|
||||||
{
|
|
||||||
this.resizing_index = Some(new_ix);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -667,36 +601,31 @@ impl Tiles {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.into_any_element()
|
.into_any_element(),
|
||||||
} else {
|
);
|
||||||
div().into_any_element()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Top resize handle
|
// Top resize handle
|
||||||
elements.push(if !is_occluded(&bottom_handle_bounds) {
|
elements.push(
|
||||||
div()
|
div()
|
||||||
.id("top-resize-handle")
|
.id("top-resize-handle")
|
||||||
.cursor_ns_resize()
|
.cursor_ns_resize()
|
||||||
.absolute()
|
.absolute()
|
||||||
.left(px(0.0))
|
.left(px(0.0))
|
||||||
.top(handle_offset)
|
.top(handle_offset)
|
||||||
.w(panel_bounds.size.width)
|
.w(item_bounds.size.width)
|
||||||
.h(HANDLE_SIZE)
|
.h(HANDLE_SIZE)
|
||||||
.on_mouse_down(
|
.on_mouse_down(
|
||||||
MouseButton::Left,
|
MouseButton::Left,
|
||||||
cx.listener({
|
cx.listener({
|
||||||
move |this, event: &MouseDownEvent, window, cx| {
|
move |this, event: &MouseDownEvent, window, cx| {
|
||||||
let last_position = event.position;
|
this.on_resize_handle_mouse_down(
|
||||||
let drag_data = ResizeDrag {
|
ResizeSide::Top,
|
||||||
side: ResizeSide::Top,
|
item_id,
|
||||||
last_position,
|
item_bounds,
|
||||||
last_bounds: panel_bounds,
|
event,
|
||||||
};
|
window,
|
||||||
this.update_resizing_drag(drag_data, window, cx);
|
cx,
|
||||||
if let Some((_, new_ix)) = this.bring_to_front(this.resizing_index, cx)
|
);
|
||||||
{
|
|
||||||
this.resizing_index = Some(new_ix);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -728,36 +657,31 @@ impl Tiles {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.into_any_element()
|
.into_any_element(),
|
||||||
} else {
|
);
|
||||||
div().into_any_element()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Bottom resize handle
|
// Bottom resize handle
|
||||||
elements.push(if !is_occluded(&bottom_handle_bounds) {
|
elements.push(
|
||||||
div()
|
div()
|
||||||
.id("bottom-resize-handle")
|
.id("bottom-resize-handle")
|
||||||
.cursor_ns_resize()
|
.cursor_ns_resize()
|
||||||
.absolute()
|
.absolute()
|
||||||
.left(px(0.0))
|
.left(px(0.0))
|
||||||
.bottom(handle_offset)
|
.bottom(handle_offset)
|
||||||
.w(panel_bounds.size.width)
|
.w(item_bounds.size.width)
|
||||||
.h(HANDLE_SIZE)
|
.h(HANDLE_SIZE)
|
||||||
.on_mouse_down(
|
.on_mouse_down(
|
||||||
MouseButton::Left,
|
MouseButton::Left,
|
||||||
cx.listener({
|
cx.listener({
|
||||||
move |this, event: &MouseDownEvent, window, cx| {
|
move |this, event: &MouseDownEvent, window, cx| {
|
||||||
let last_position = event.position;
|
this.on_resize_handle_mouse_down(
|
||||||
let drag_data = ResizeDrag {
|
ResizeSide::Bottom,
|
||||||
side: ResizeSide::Bottom,
|
item_id,
|
||||||
last_position,
|
item_bounds,
|
||||||
last_bounds: panel_bounds,
|
event,
|
||||||
};
|
window,
|
||||||
this.update_resizing_drag(drag_data, window, cx);
|
cx,
|
||||||
if let Some((_, new_ix)) = this.bring_to_front(this.resizing_index, cx)
|
);
|
||||||
{
|
|
||||||
this.resizing_index = Some(new_ix);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -788,13 +712,11 @@ impl Tiles {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.into_any_element()
|
.into_any_element(),
|
||||||
} else {
|
);
|
||||||
div().into_any_element()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Corner resize handle
|
// Corner resize handle
|
||||||
elements.push(if !is_occluded(&corner_handle_bounds) {
|
elements.push(
|
||||||
div()
|
div()
|
||||||
.child(
|
.child(
|
||||||
Icon::new(IconName::ResizeCorner)
|
Icon::new(IconName::ResizeCorner)
|
||||||
|
|
@ -816,18 +738,14 @@ impl Tiles {
|
||||||
MouseButton::Left,
|
MouseButton::Left,
|
||||||
cx.listener({
|
cx.listener({
|
||||||
move |this, event: &MouseDownEvent, window, cx| {
|
move |this, event: &MouseDownEvent, window, cx| {
|
||||||
let last_position = event.position;
|
this.on_resize_handle_mouse_down(
|
||||||
let drag_data = ResizeDrag {
|
ResizeSide::BottomRight,
|
||||||
side: ResizeSide::BottomRight,
|
item_id,
|
||||||
last_position,
|
item_bounds,
|
||||||
last_bounds: panel_bounds,
|
event,
|
||||||
};
|
window,
|
||||||
this.update_resizing_drag(drag_data, window, cx);
|
cx,
|
||||||
if let Some((_, new_ix)) =
|
);
|
||||||
this.bring_to_front(this.resizing_index, cx)
|
|
||||||
{
|
|
||||||
this.resizing_index = Some(new_ix);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -873,14 +791,35 @@ impl Tiles {
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.into_any_element()
|
.into_any_element(),
|
||||||
} else {
|
);
|
||||||
div().into_any_element()
|
|
||||||
});
|
|
||||||
|
|
||||||
elements
|
elements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_resize_handle_mouse_down(
|
||||||
|
&mut self,
|
||||||
|
side: ResizeSide,
|
||||||
|
item_id: EntityId,
|
||||||
|
item_bounds: Bounds<Pixels>,
|
||||||
|
event: &MouseDownEvent,
|
||||||
|
_: &mut Window,
|
||||||
|
cx: &mut Context<'_, Self>,
|
||||||
|
) {
|
||||||
|
let last_position = event.position;
|
||||||
|
self.resizing_id = Some(item_id);
|
||||||
|
self.resizing_drag_data = Some(ResizeDrag {
|
||||||
|
side,
|
||||||
|
last_position,
|
||||||
|
last_bounds: item_bounds,
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(new_id) = self.bring_to_front(self.resizing_id, cx) {
|
||||||
|
self.resizing_id = Some(new_id);
|
||||||
|
}
|
||||||
|
cx.stop_propagation();
|
||||||
|
}
|
||||||
|
|
||||||
/// Produce the drag-bar element for the given panel item
|
/// Produce the drag-bar element for the given panel item
|
||||||
fn render_drag_bar(
|
fn render_drag_bar(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -888,78 +827,57 @@ impl Tiles {
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
entity_id: EntityId,
|
entity_id: EntityId,
|
||||||
item: &TileItem,
|
item: &TileItem,
|
||||||
is_occluded: &impl Fn(&Bounds<Pixels>) -> bool,
|
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
let drag_bar_bounds = Bounds::new(
|
let item_id = item.id;
|
||||||
item.bounds.origin,
|
let item_bounds = item.bounds;
|
||||||
Size {
|
|
||||||
width: item.bounds.size.width,
|
|
||||||
height: DRAG_BAR_HEIGHT,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if !is_occluded(&drag_bar_bounds) {
|
h_flex()
|
||||||
h_flex()
|
.id("drag-bar")
|
||||||
.id("drag-bar")
|
.absolute()
|
||||||
.absolute()
|
.w_full()
|
||||||
.w_full()
|
.h(DRAG_BAR_HEIGHT)
|
||||||
.h(DRAG_BAR_HEIGHT)
|
.bg(cx.theme().transparent)
|
||||||
.bg(cx.theme().transparent)
|
.on_mouse_down(
|
||||||
.on_mouse_down(
|
MouseButton::Left,
|
||||||
MouseButton::Left,
|
cx.listener(move |this, event: &MouseDownEvent, _, cx| {
|
||||||
cx.listener(move |this, event: &MouseDownEvent, window, cx| {
|
let inner_pos = event.position - this.bounds.origin;
|
||||||
let last_position = event.position;
|
this.dragging_id = Some(item_id);
|
||||||
this.update_initial_position(last_position, window, cx);
|
this.dragging_initial_mouse = inner_pos;
|
||||||
if let Some((_, new_ix)) = this.bring_to_front(this.dragging_index, cx) {
|
this.dragging_initial_bounds = item_bounds;
|
||||||
this.dragging_index = Some(new_ix);
|
|
||||||
}
|
if let Some(new_id) = this.bring_to_front(Some(item_id), cx) {
|
||||||
}),
|
this.dragging_id = Some(new_id);
|
||||||
)
|
}
|
||||||
.on_drag(DragMoving(entity_id), |drag, _, _, cx| {
|
}),
|
||||||
cx.stop_propagation();
|
)
|
||||||
cx.new(|_| drag.clone())
|
.on_drag(DragMoving(entity_id), |drag, _, _, cx| {
|
||||||
})
|
cx.stop_propagation();
|
||||||
.on_drag_move(cx.listener(
|
cx.new(|_| drag.clone())
|
||||||
move |this, e: &DragMoveEvent<DragMoving>, window, cx| match e.drag(cx) {
|
})
|
||||||
|
.on_drag_move(
|
||||||
|
cx.listener(move |this, e: &DragMoveEvent<DragMoving>, window, cx| {
|
||||||
|
match e.drag(cx) {
|
||||||
DragMoving(id) => {
|
DragMoving(id) => {
|
||||||
if *id != entity_id {
|
if *id != entity_id {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.update_position(e.event.position, window, cx);
|
this.update_position(e.event.position, window, cx);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
))
|
}),
|
||||||
.into_any_element()
|
)
|
||||||
} else {
|
.into_any_element()
|
||||||
div().into_any_element()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_panel(
|
fn render_panel(
|
||||||
&mut self,
|
&mut self,
|
||||||
item: &TileItem,
|
item: &TileItem,
|
||||||
ix: usize,
|
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
let entity_id = cx.entity_id();
|
let entity_id = cx.entity_id();
|
||||||
|
let item_id = item.id;
|
||||||
let panel_view = item.panel.view();
|
let panel_view = item.panel.view();
|
||||||
let is_occluded = {
|
|
||||||
let panels = self.panels.clone();
|
|
||||||
move |bounds: &Bounds<Pixels>| {
|
|
||||||
let this_z = panels[ix].z_index;
|
|
||||||
let this_ix = ix;
|
|
||||||
panels.iter().enumerate().any(|(sub_ix, other_item)| {
|
|
||||||
if sub_ix == this_ix {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let other_is_above = (other_item.z_index > this_z)
|
|
||||||
|| (other_item.z_index == this_z && sub_ix > this_ix);
|
|
||||||
|
|
||||||
other_is_above && other_item.bounds.intersects(bounds)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.occlude()
|
.occlude()
|
||||||
|
|
@ -974,13 +892,22 @@ impl Tiles {
|
||||||
.h(item.bounds.size.height + px(1.))
|
.h(item.bounds.size.height + px(1.))
|
||||||
.rounded(cx.theme().radius)
|
.rounded(cx.theme().radius)
|
||||||
.child(h_flex().overflow_hidden().size_full().child(panel_view))
|
.child(h_flex().overflow_hidden().size_full().child(panel_view))
|
||||||
.children(self.render_resize_handles(window, cx, entity_id, &item, &is_occluded))
|
.children(self.render_resize_handles(window, cx, entity_id, &item))
|
||||||
.child(self.render_drag_bar(window, cx, entity_id, &item, &is_occluded))
|
.child(self.render_drag_bar(window, cx, entity_id, &item))
|
||||||
|
.on_mouse_down(
|
||||||
|
MouseButton::Left,
|
||||||
|
cx.listener(move |this, _, _, _| {
|
||||||
|
this.dragging_id = Some(item_id);
|
||||||
|
}),
|
||||||
|
)
|
||||||
// Here must be mouse up for avoid conflict with Drag event
|
// Here must be mouse up for avoid conflict with Drag event
|
||||||
.on_mouse_up(
|
.on_mouse_up(
|
||||||
MouseButton::Left,
|
MouseButton::Left,
|
||||||
cx.listener(move |this, _, _, cx| {
|
cx.listener(move |this, _, _, cx| {
|
||||||
this.bring_to_front(Some(ix), cx);
|
if this.dragging_id == Some(item_id) {
|
||||||
|
this.dragging_id = None;
|
||||||
|
this.bring_to_front(Some(item_id), cx);
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -988,15 +915,15 @@ impl Tiles {
|
||||||
/// Handle the mouse up event to finalize drag or resize operations
|
/// Handle the mouse up event to finalize drag or resize operations
|
||||||
fn on_mouse_up(&mut self, _: &mut Window, cx: &mut Context<'_, Tiles>) {
|
fn on_mouse_up(&mut self, _: &mut Window, cx: &mut Context<'_, Tiles>) {
|
||||||
// Check if a drag or resize was active
|
// Check if a drag or resize was active
|
||||||
if self.dragging_index.is_some()
|
if self.dragging_id.is_some()
|
||||||
|| self.resizing_index.is_some()
|
|| self.resizing_id.is_some()
|
||||||
|| self.resizing_drag_data.is_some()
|
|| self.resizing_drag_data.is_some()
|
||||||
{
|
{
|
||||||
let mut changes_to_push = vec![];
|
let mut changes_to_push = vec![];
|
||||||
|
|
||||||
// Handle dragging
|
// Handle dragging
|
||||||
if let Some(index) = self.dragging_index {
|
if let Some(dragging_id) = self.dragging_id {
|
||||||
if let Some(item) = self.panels.get(index) {
|
if let Some(item) = self.panel(&dragging_id) {
|
||||||
let initial_bounds = self.dragging_initial_bounds;
|
let initial_bounds = self.dragging_initial_bounds;
|
||||||
let current_bounds = item.bounds;
|
let current_bounds = item.bounds;
|
||||||
if initial_bounds.origin != current_bounds.origin
|
if initial_bounds.origin != current_bounds.origin
|
||||||
|
|
@ -1015,9 +942,9 @@ impl Tiles {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle resizing
|
// Handle resizing
|
||||||
if let Some(index) = self.resizing_index {
|
if let Some(resizing_id) = self.resizing_id {
|
||||||
if let Some(drag_data) = &self.resizing_drag_data {
|
if let Some(drag_data) = &self.resizing_drag_data {
|
||||||
if let Some(item) = self.panels.get(index) {
|
if let Some(item) = self.panel(&resizing_id) {
|
||||||
let initial_bounds = drag_data.last_bounds;
|
let initial_bounds = drag_data.last_bounds;
|
||||||
let current_bounds = item.bounds;
|
let current_bounds = item.bounds;
|
||||||
if initial_bounds.size != current_bounds.size {
|
if initial_bounds.size != current_bounds.size {
|
||||||
|
|
@ -1102,8 +1029,7 @@ impl Render for Tiles {
|
||||||
.children(
|
.children(
|
||||||
panels
|
panels
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.map(|item| self.render_panel(&item, window, cx)),
|
||||||
.map(|(ix, item)| self.render_panel(&item, ix, window, cx)),
|
|
||||||
)
|
)
|
||||||
.child({
|
.child({
|
||||||
canvas(
|
canvas(
|
||||||
|
|
@ -1123,18 +1049,6 @@ impl Render for Tiles {
|
||||||
this.on_mouse_up(window, cx);
|
this.on_mouse_up(window, cx);
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.on_mouse_down(
|
|
||||||
MouseButton::Left,
|
|
||||||
cx.listener(move |this, event: &MouseDownEvent, _, cx| {
|
|
||||||
if this.resizing_index.is_none() && this.dragging_index.is_none() {
|
|
||||||
let position = event.position;
|
|
||||||
if let Some((index, _)) = this.find_at_position(position) {
|
|
||||||
this.bring_to_front(Some(index), cx);
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.absolute()
|
.absolute()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue