tiles: Fix back panels flick when hover on top panel in tiles. (#708)

- Move resize handle out of the panel to avoid conflict with scrollbar.
This commit is contained in:
Jason Lee 2025-03-11 19:56:50 +08:00 committed by GitHub
parent d8ec1ee516
commit df94217999
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -28,7 +28,7 @@ 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.);
const HANDLE_SIZE: Pixels = px(12.0); const HANDLE_SIZE: Pixels = px(5.0);
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
struct TileChange { struct TileChange {
@ -133,7 +133,6 @@ pub struct Tiles {
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_index: Option<usize>,
active_index: Option<usize>,
resizing_drag_data: Option<ResizeDrag>, resizing_drag_data: Option<ResizeDrag>,
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
history: History<TileChange>, history: History<TileChange>,
@ -193,7 +192,6 @@ impl Tiles {
history: History::new().group_interval(std::time::Duration::from_millis(100)), history: History::new().group_interval(std::time::Duration::from_millis(100)),
scroll_state: Rc::new(Cell::new(ScrollbarState::default())), scroll_state: Rc::new(Cell::new(ScrollbarState::default())),
scroll_handle: ScrollHandle::default(), scroll_handle: ScrollHandle::default(),
active_index: None,
} }
} }
@ -498,11 +496,7 @@ impl Tiles {
/// Returns the active panel, if any. /// Returns the active panel, if any.
pub fn active_panel(&self, cx: &App) -> Option<Arc<dyn PanelView>> { pub fn active_panel(&self, cx: &App) -> Option<Arc<dyn PanelView>> {
let Some(active_index) = self.active_index else { self.panels.last().and_then(|item| {
return None;
};
self.panels.get(active_index).and_then(|item| {
if let Ok(tab_panel) = item.panel.view().downcast::<TabPanel>() { if let Ok(tab_panel) = item.panel.view().downcast::<TabPanel>() {
tab_panel.read(cx).active_panel(cx) tab_panel.read(cx).active_panel(cx)
} else if let Ok(_) = item.panel.view().downcast::<StackPanel>() { } else if let Ok(_) = item.panel.view().downcast::<StackPanel>() {
@ -513,15 +507,6 @@ impl Tiles {
}) })
} }
fn set_active_index(&mut self, index: Option<usize>, cx: &mut Context<Self>) {
if self.active_index == index {
return;
}
self.active_index = index;
cx.notify();
}
/// Produce a vector of AnyElement representing the three possible resize handles /// Produce a vector of AnyElement representing the three possible resize handles
fn render_resize_handles( fn render_resize_handles(
&mut self, &mut self,
@ -533,8 +518,8 @@ impl Tiles {
) -> Vec<AnyElement> { ) -> Vec<AnyElement> {
let panel_bounds = item.bounds; let panel_bounds = item.bounds;
let right_handle_bounds = Bounds::new( let right_handle_bounds = Bounds::new(
panel_bounds.origin + point(panel_bounds.size.width - HANDLE_SIZE.half(), px(0.0)), panel_bounds.origin + point(panel_bounds.size.width - HANDLE_SIZE, px(0.0)),
size(HANDLE_SIZE.half(), panel_bounds.size.height), size(HANDLE_SIZE, panel_bounds.size.height),
); );
let bottom_handle_bounds = Bounds::new( let bottom_handle_bounds = Bounds::new(
@ -550,6 +535,7 @@ impl Tiles {
), ),
size(HANDLE_SIZE.half(), HANDLE_SIZE.half()), size(HANDLE_SIZE.half(), HANDLE_SIZE.half()),
); );
let handle_offset = -HANDLE_SIZE + px(1.);
let mut elements = Vec::new(); let mut elements = Vec::new();
@ -557,10 +543,10 @@ impl Tiles {
elements.push(if !is_occluded(&right_handle_bounds) { elements.push(if !is_occluded(&right_handle_bounds) {
div() div()
.id("right-resize-handle") .id("right-resize-handle")
.cursor_col_resize() .cursor_ew_resize()
.absolute() .absolute()
.top(px(0.0)) .top_0()
.right(-HANDLE_SIZE.half()) .right(handle_offset)
.w(HANDLE_SIZE) .w(HANDLE_SIZE)
.h(panel_bounds.size.height) .h(panel_bounds.size.height)
.on_mouse_down( .on_mouse_down(
@ -614,10 +600,10 @@ impl Tiles {
elements.push(if !is_occluded(&bottom_handle_bounds) { elements.push(if !is_occluded(&bottom_handle_bounds) {
div() div()
.id("bottom-resize-handle") .id("bottom-resize-handle")
.cursor_row_resize() .cursor_ns_resize()
.absolute() .absolute()
.left(px(0.0)) .left(px(0.0))
.bottom(-HANDLE_SIZE.half()) .bottom(handle_offset)
.w(panel_bounds.size.width) .w(panel_bounds.size.width)
.h(HANDLE_SIZE) .h(HANDLE_SIZE)
.on_mouse_down( .on_mouse_down(
@ -667,64 +653,74 @@ impl Tiles {
// Corner resize handle // Corner resize handle
elements.push(if !is_occluded(&corner_handle_bounds) { elements.push(if !is_occluded(&corner_handle_bounds) {
div() div()
.id("corner-resize-handle")
.cursor_nwse_resize()
.absolute()
.right(px(3.))
.bottom(px(3.))
.size_3()
.child( .child(
Icon::new(IconName::ResizeCorner) Icon::new(IconName::ResizeCorner)
.size_3() .size_3()
.absolute()
.right(px(1.))
.bottom(px(1.))
.text_color(cx.theme().muted_foreground.opacity(0.5)), .text_color(cx.theme().muted_foreground.opacity(0.5)),
) )
.rounded(cx.theme().radius) .child(
.on_mouse_down( div()
MouseButton::Left, .id("corner-resize-handle")
cx.listener({ .cursor_nwse_resize()
move |this, event: &MouseDownEvent, window, cx| { .absolute()
let last_position = event.position; .right(handle_offset)
let drag_data = ResizeDrag { .bottom(handle_offset)
axis: ResizeAxis::Both, .size_3()
last_position, .on_mouse_down(
last_bounds: panel_bounds, MouseButton::Left,
}; cx.listener({
this.update_resizing_drag(drag_data, window, cx); move |this, event: &MouseDownEvent, window, cx| {
if let Some((_, new_ix)) = this.bring_to_front(this.resizing_index, cx) let last_position = event.position;
{ let drag_data = ResizeDrag {
this.resizing_index = Some(new_ix); axis: ResizeAxis::Both,
} last_position,
} last_bounds: panel_bounds,
}), };
) this.update_resizing_drag(drag_data, window, cx);
.on_drag(DragResizing(entity_id), |drag, _, _, cx| { if let Some((_, new_ix)) =
cx.stop_propagation(); this.bring_to_front(this.resizing_index, cx)
cx.new(|_| drag.clone()) {
}) this.resizing_index = Some(new_ix);
.on_drag_move(cx.listener( }
move |this, e: &DragMoveEvent<DragResizing>, window, cx| match e.drag(cx) {
DragResizing(id) => {
if *id != entity_id {
return;
}
if let Some(ref drag_data) = this.resizing_drag_data {
if drag_data.axis != ResizeAxis::Both {
return;
} }
let pos = e.event.position; }),
let delta_x = pos.x - drag_data.last_position.x; )
let delta_y = pos.y - drag_data.last_position.y; .on_drag(DragResizing(entity_id), |drag, _, _, cx| {
let new_width = (drag_data.last_bounds.size.width + delta_x) cx.stop_propagation();
.max(MINIMUM_SIZE.width); cx.new(|_| drag.clone())
let new_height = (drag_data.last_bounds.size.height + delta_y) })
.max(MINIMUM_SIZE.height); .on_drag_move(cx.listener(
this.resize_height(new_height, window, cx); move |this, e: &DragMoveEvent<DragResizing>, window, cx| {
this.resize_width(new_width, window, cx); match e.drag(cx) {
} DragResizing(id) => {
} if *id != entity_id {
}, return;
)) }
if let Some(ref drag_data) = this.resizing_drag_data {
if drag_data.axis != ResizeAxis::Both {
return;
}
let pos = e.event.position;
let delta_x = pos.x - drag_data.last_position.x;
let delta_y = pos.y - drag_data.last_position.y;
let new_width = (drag_data.last_bounds.size.width
+ delta_x)
.max(MINIMUM_SIZE.width);
let new_height = (drag_data.last_bounds.size.height
+ delta_y)
.max(MINIMUM_SIZE.height);
this.resize_height(new_height, window, cx);
this.resize_width(new_width, window, cx);
}
}
}
},
)),
)
.into_any_element() .into_any_element()
} else { } else {
div().into_any_element() div().into_any_element()
@ -814,6 +810,7 @@ impl Tiles {
}; };
v_flex() v_flex()
.occlude()
.bg(cx.theme().background) .bg(cx.theme().background)
.border_1() .border_1()
.border_color(cx.theme().border) .border_color(cx.theme().border)
@ -823,15 +820,15 @@ impl Tiles {
// More 1px to account for the border width when 2 panels are too close // More 1px to account for the border width when 2 panels are too close
.w(item.bounds.size.width + px(1.)) .w(item.bounds.size.width + px(1.))
.h(item.bounds.size.height + px(1.)) .h(item.bounds.size.height + px(1.))
.overflow_hidden()
.rounded(cx.theme().radius) .rounded(cx.theme().radius)
.child(h_flex().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, &is_occluded))
.child(self.render_drag_bar(window, cx, entity_id, &item, &is_occluded)) .child(self.render_drag_bar(window, cx, entity_id, &item, &is_occluded))
// 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.set_active_index(Some(ix), cx); this.bring_to_front(Some(ix), cx);
}), }),
) )
} }